вторник, 27 декабря 2011 г.

Десять способов потерять ценных сотрудников

Сложно ли таким выдающимся IT-компаниям как Yahoo! или более выраженным конгломератам таким, как, скажем, General Electrics или Home Depot сохранять свои лучшие кадры в неприкосновенности. Не так давно на GigaOM обсуждались проблемы Yahoo с ее бесприбыльным графиком рыночной строимости, бенефитами отдельных «звезд» внутри компании и утечками лучших кадров на неизвестные стартап-проекты.
И еще, имеют ли подобные компании какие-либо преимущества по удерживанию своих талантов. Я могу отметить как положительные, так и отрицательные моменты, которые присутствуют в управлении талантливыми сотрудниками в больших компаниях. Вот мой список топ-10, пункты которого «помогают» компаниям потерять свои таланты.
1. Бюрократия большой компании. Это пожалуй причина №1 которую мы слышим, после того, как ценные сотрудники покидают компанию. Но на самом деле, за этой причиной стоит некая иная причина. Никому не нравятся правила, которые не имеют смысла. Но когда таланты жалуются по этому поводу, это является знаком того, что они считают, что им не дали высказаться. Они может быть и говорили что-то ранее. Однако действительно талантливые сотрудники, обязательной сказали бы: «постойте, давайте проверим».
2. Остутствие попыток найти нечто, что было бы способно зажечь умы талантов. Большая компания представляет собой огромный, постоянно двигающийся механизм. Поэтому, как правило, нет таких специальных людей, которые бегали бы вокруг «нужных» сотрудников и спрашивали бы их без умолку, нравится ли им их текущий проект или хотели бы ли они заниматься чем то новым, что им было бы интересно и таким образом помогать компании. Отдел кадров обычно слишком занят, что бы тратить время на подобную деятельность. Начальники также разрываются на части по другим, очевидно, не менее важным делам, и вот этот вопрос по поиску заинтересованности талантов переходит из разряда обязательного в разряд желательного. Но до тех пор пока это не будет обязательным, может сказать «адиос» вашим лучшим людям. Талантливые люди на первое место ставят не власть и деньги, а скорее возможность принять участие в чем-то невероятном, способном изменить мир. Такие вещи их действительно заводят. Большие компании как правило не уделяют должного внимания этому вопросу.
3. Недостачный ежегодный анализ результативности. Вы бы изумились, если бы узнали как большие компании проводят ежегодный анализ результативности. Если он есть, то часто это сводится к тому, что в срочном порядке заполняется бланк отчетности «для галочки» и тут же отсылается в отдел кадров. Это оставляет впечатление, что начальство - и, соответственно, компанию вцелом – не интересует отдаленное будущее своих сотрудников. Если вы настолько талантливы, что вы тут делаете? И это приводит к пункту 4...
4. Нету обсуждения посвященного развитию карьеры. Тут запрятан секрет большинства боссов: большинство сотрудников не имеют понятия чем они будут заниматься через пять лет. По-нашим подсчетам менее пяти процентов людей смогут ответить на этот вопрос. Однако каждый из нас хочет, что бы разговор о нашем будущем непременно состоялся. Большинство руководителей никогда не затрагивают эту тему со своими подчиненными, даже с самыми талантливыми. Это представляет собой великолепную возможность для вас и вашей организации изменить ситуацию, если вы возьмете этот вопрос в оборот. Затрагивайте с каждым из ваших сотрудников данный вопрос раз а лучше два раза в год. Если ценные вам сотрудники знают, что вы думаете о их будущем, это пойдет только на пользу.
5. Меняющиеся прихоти / стратегические приоритеты. Я аплодирую компаниям, которые пытаются создать инкубатор или «кирпичный дом» для своих самых ценных сотрудников, используя для этого новые занимательные проекты. Проблема большинства организаций заключается в том, что у них не расставлены стратегические приоритеты. То есть, они могут подумать о подобном инкубаторе, но начать его внедрять только спустя год или два. Талантливые люди ненавидят томится в ожидании. Если вы назначили ценных сотрудников на проект, они вправе получить от проекта то, что было обещано ранее.
6. Недостаток ответственности и/или разговоров о том как нужно работать. Хотя вы не можете позволить что бы талантливые люди «валяли дурака», также будет и ошибкой пустить на самотек проекты, управляемые вашими талантами. Речь не идет о том, что вы должны лезть не в свои дела и говорить кому что и как делать. Однако, талантливый человек требует от окружающих ответственности и совсем не против нести ответственность за свои проекты. Поэтому, практикуйте небольшие митинги с вашими талантами относительно текущего состояния дел. Они будут ценить ваше внимание к их делам – но только если ваше вмешательство будет в рамках допустимого.
7. Талантливые люди любят других талантливых людей. Какие люди окружают ваши таланты? Многие организации пристраивают к выдающимся личностям более посредственных сотрудников. Для этого начальство находит тысячу и одну причину: «Ну этого парня сложно было пристроить куда-то еще...», «Давайте пока он тут побудет...». Однако, как показывают опросы талантливых сотрудников при увольнении из крупных организаций, многие увольнялись из-за того что не сработались со своими не такими талантливыми коллегами. Так что, если вы хотите сохранить ваши выдающиеся кадры при себе, побеспокойтесь что бы они были окружены не менее выдающимися личностями.
8. Момент пропущенной цели. Это может казаться очевидным, но тем не менее, насколько радужное будущее у вашей организации? Какой стратегии вы следуете? К какой цели должны стремится талантливые сотрудники вашей организации? Они могут принимать участие в обсуждение этой цели? Если ответ – нет, это нужно немедленно исправлять.
9. Недостаток открытого взаимодействия. Талантливые сотрудники хотят говорить о своих идеях и хотят быть услышанными. Но, это желание часто не согласуется со стратегиями многих организаций – и часто подобные инициативы тут же подавляются. Ваши лучшие люди в такой ситуации покинут вас и рядом с вами останутся люди всегда говорящие «Да» по любому вопросу которые вы им подкинете. Вам же следует научиться выслушивать и другие точки зрения, отличные от ваших – и брать из всех предложенных решений только самое лучшее.
10. Кто здесь начальник? Если несколько людей подчиненных одному руководителю увольняются со своих позиций – это плохой знак. Часто в подобных случаях нужно прийти и серьёзно поговорить с таким руководителем. Но, к сожалению, такая стратегия работает не всегда и дает положительный результат только в одном случае из трех. Лучшим решением будет перевод такого «начальника» на другую позицию в организации, туда, где он не сможет добраться до ваших самых талантливых.
В реальности не всегда виновата организация. Талантливые люди также несут ответственность по этому вопросу не меньше организации-работодателя. Однако, в наши дни талантливых сотрудников очень немного и поэтому на коне будут те организации, которые сами предпримут необходимые действия, что бы устранить проблемы описанные в данной статье-рекомендации и не будут дожидаться пока кто-то придет к ним и подскажет что и как нужно делать.

пятница, 25 ноября 2011 г.

Так что же нам готовит Windows 8?


Статья базируется на публикации Дино Эспозито "What Exactly is Windows 8?"

Metro – одно из знаковых нововведений для разработчиков, которое Microsoft представит в релизе новой версии своей операционной системы.

В сентябре Microsoft продемонстрировала первое превью разрабатываемой Windows 8. Новая версия операционной системы предназначена не только для PC, она также может использоваться и на планшетных устройствах. С точки зрения разработчика, Windows 8: система двух режимов. Первый режим базируется на продолжении развития предыдущих версий Windows, и предоставляет привычный пользовательский интерфейс и возможности для разработки. Второй режим – это абсолютно новое решение пользовательского интерфейса, которое базируется на новом системном API.
Это новое решение Windows 8 называется Metro. Оно построено на пользовательском интерфейсе Windows Phone. Оно также предлагает набор новых приложений, которые выполняются с помощью нового системного API Windows Runtime (или сокращенно WinRT).
Из-за наличия двух режимов пользовательского интерфейса в Windows 8 – обычного и Metro – новые приложения также в перспективе будут делится на две соответствующие категории. Это очевидное и простое требование породило множество дисскусий, которые стали источниками заблуждений и необоснованных догадок.
Вот несколько ключевых вопросов, которые мучают разработчиков по всему миру, и на которые пока нету четких и вразумительных ответов:
·         Какую роль играет фреймворк .NET при реализации приложений Metro?
·         Что вообще требуется для написания Metro приложений?
·         И более глобально, какое будущее ждет платформу Windows вцелом?
·         Какое будущее у меня как, разработчика Windows?
Неопределенность, которая сейчас существует является отличным поводом для появления всевозможных страхов, слухов и сомнений. В данной публикации мы попробуем проанализировать информацию, которую Microsoft предоставила на первой презентации Windows 8, и постараемся прийти к выводу, что все страхи не имеют под собой никаких оснований.

Новое представление Windows 7

Microsoft гарантирует обратную совместимость. Таким образом все приложения, которые были написаны ранее, будут работать и на новой операционной системе так же как и прежде. Говоря о Metro, нужно понимать, что это новое семейство приложений. Windows 8 может работать в двух режимах, которые можно переключать в зависимости от своих предпочтений: стандартный режим, который и выглядит и работает так, как это было в предыдущих версиях Windows, но с небольшими обновлениями ключевых компонентов системы, и новый режим, который базируется на Metro. В Metro присутствует нативная поддержка тач-скрина, новая система рантайм, и новые подходы в программировании. Metro в первую очередь проектировалось для планшетных устройств, однако также доступно и для стандартных PC.
Можно посмтреть на данный вопрос под другим углом: классический рабочий стол Windows – это просто другая интерпретация тех возможностей которые предоставляют новые приложения Metro.
Это похоже на революцию? Можно привести мнение Стивена Синофски (Steven Sinofsky), президента подразделения Windows и Windows Live. Он говорит, что Windows 8  это попытка “придать новый облик Windows”.
Microsoft не может игнорировать существующие приложения и потребности пользователей Windows 7. Поэтому, для того, что бы всецело перейти на новый пользовательский интерфейс, и сделать его единственно возможным вариантом для взаимодействия с пользователем в новой версии Windows, необходимо приложить чрезвычайно много усилий и осущесвить очень много изменений. Поэтому Microsoft пошла на компромисное решение, принимающее в расчет наличие двух режимов.
Будет ли переход на Windows 8 чем то похожим на изменения которые в свое время принесла Windows 95? Win 95 обновила существовавшие тогда программы заместив собой устаревшую в то время консольную операционную систему MS-DOS. В то время это было начало новой вехи в развитии области операционных систем. Metro является новой вехой в развитии только лишь платформы Windows. Новшества, которые придут вместе с Windows 8 будут значительно проще и менее глобальны.

Вызов для разработчиков

Существующие приложения Windows не нужно портировать под Metro и разработчики программного обеспечения могут по прежнему писать свои программы используя классические приемы, средства разработки и применяемые технологии – фреймворк .NET и соответствующие языки программирования.
Приложения Metro потребуют нового подхода к процессу разработки, нового API и новой системы рантайм, но использоваться будут уже знакомые языки программирования и средства разработки. Например можно использовать HTML5 в связке с JavaScript и CSS3. Также можно использовать C++, Visual Basic и C#. Во всех случаях, XAML остается одним из вариантов для разработки пользовательского интерфейса.
В терминах функциоанальности, API для приложений Metro имеет много общего с API для приложений .NET: некоторые пространства имен будут переименованы и некоторые классы  перемещены в другие компоненты. Важным моментом, который нельзя упустить из виду, является тот факт, что приложения Metro выполняются с использованием нового рантайма, который является оберткой над сервисами ядра Windows в манере похожей на Win32. Фреймворк .NET общается с системой через P/Invoke вызовы функций Win32. WinRT – рантайм использоуемый Metro – обращается к нативным сервисам системы более непостредственно, и использует другие подходы для их инкапсуляции.
Приложения Metro следуют логике мобильных приложений. Они никогда не выполняются непонятно как, не зависают. Говоря далее, нужно отметить, что приложения Metro распространяются через специальный маркет, благодаря чему каждое приложение проходит через процесс получения сертификата качества Microsoft. Получение лицензии на использование программы осуществляется с привязкой к конкретному пользователю, а не к машине. Настройки по умолчанию используют облако для хранения данных. И, наконец, приложение Metro должно содержать информацию о аппаратном обеспечении, на котором оно может выплнятся.
Таким образом, написание приложения Metro похоже на написание приложений под мобильные платфрмы, такие, как, скажем, Windows Phone 7. Природа и структура приложений также отличается от традиционных .NET приложений, поскольку приложения Metro призваны быть более простыми, более интерактивными и сконцентрироваными на содержимом.
Ну что? Несет ли Windows 8 в себе большие изменения для пользователей и разработчиков? Являются ли Windows 8 и приложения Metro тем, на что нужно немедленно сконцентрировать все свое внимание и воспринимать как догму? Очевидно, ответ на оба вопроса будет один – нет, я так не думаю. Windows 8 – эволюционное развитие платформы Windows. Metro – новшество, которое приходит вместе с Windows 8. Для разработчика – это очередная новая платформа, которая возможно получит развитие в будущем.

пятница, 7 октября 2011 г.

Orange Heap - Еще один бесплатный обфускатор для .NET


В последнее время я активно занимался изучением принципов обфускации и существующих приемов модификации кода, направленных на затруднение процесса реверс-инжиниринга .NET программ. Отчасти, результатом всего этого процесса исследования стал цилк статей “Как работают обфускаторы” (часть 1 (Переименование и шифрование), часть 2 (Изменения внутренней логики), часть 3 (Дополнительные возможности)). Данным циклом я занимался несколько последних месяцев. Если вы интересуетесь вопросами обфускации, крайне рекомендую его к прочтению.  Статьи легко читаются и содержат много полезной и наглядной информации.
Однако одним только циклом дело не закончилось. Для того что бы прочувствовать все нюансы, которые возникают с процессе создания средства обфускации я пошел немного дальше и решил написать свой собственный .NET обфускатор. Результат не заставил себя долго ждать и уже в начале сентября я выпустил первый публичный релиз, спустя четыре с половиной месяца после начала работы над проектом. А через две недели выпустил небольшое обновление с исправлениями ошибок, которые были найдены и исправлены после релиза.
На самом деле на реализацию конечного продукта было потрачено с апреля по сегодняшний день всего лишь чуть более 160 часов. Поскольку у меня есть основная работа, времени на собственные проекты не так то и много. В связи с этим реализация обфускатора растянулась на несколько месяцев. Уверен, если бы я занмался проектом фул-тайм, мне бы с лихвой хватило трех-четырех недель на доведения проекта с нуля до того состояния которое есть сегодня.
Посмотреть на результат моих трудов и даже воспользоваться ими можно зайдя на сайт поддержки продукта. Ссылка на сайт: http://orangeheap.blogspot.com.
А теперь немного о том, как получилось то, что получилось, и, собственно, что же все таки получилось?
***
В основу работы обфускатора Orange Heap положен фреймворк CCI (Common Compiler Infrastructure http://ccimetadata.codeplex.com) С помощью него осуществляется доступ к метаданным программы и производятся необходимые изменения. Для таких целей, помимо CCI существует еще как минимум один доступный инструмент: Mono.Cecil. Однако мой выбор пал именно на CCI. Я не знаю лучше ли он чем Mono.Cecil и если лучше то чем. В тот момент, когда я только начинал заниматься проектом, для меня оба фреймворка былы чем-то неизвестным и в процессе выбора я руководствовался принципом «пальцем в небо». Единственное что я знал, так это то, что на базе Mono.Cecil уже существуют решения, в том числе и коммерческие, которые позволяют осуществлять обфускацию. Фреймворк CCI был менее исследован, и отзывов о его использовании было гораздо меньше. Но, наверное, тот факт, что CCI разрабатывался Microsoft Research подтолкнул меня к выбору именно этого фреймворка.
Ну, хватит лирики, лучше продолжим дальше разговор об Orange Heap. Продукт состоит из двух частей: консольное приложение и Windows-приложение.
С помощью Windows-приложения, можно осуществлять конфигурацию процесса обфускации. Данный процесс конфигурации позволяет указать такие опции как: нужно ли переименовывать публичные элемента и неймспейсы, нужно ли зашифровывать строки, нужно ли склеивать защищаемые сборки в одну монолитную сборку.
Помимо этого можно указать каким образом должно осуществляться переименование элементов. На сегодняшний день существует две политики переименования: нечитаемые символы (Normal policy) (в дизассемблере или рефлекторе такие имена будут отображаться в виде квадратиков) или имена вида a1, a2,.. a14,.. a445 (Developer policy). Наличие двух политик переименования связано лишь с тем, что в процессе обфускации (не только с помощью Orange Heap но и с любым другим обфускатором, в том числе и коммерческим) программа может сломаться и перестать работать так, как она работала до этого. Если такая ситуация произошла, значит, очевидно, нужно как-то изменить процесс обфускации (например запретить что-то переименовывать или шифровать)  и запустить его заново. Но в случае если все переменные, типы и методы отображаются в виде квардатиков – задача становится не очень тривиальной. В этом случае имеет смысл задавать имена в виде a1, a2, … Такие имена проще анализировать в процессе поломки программы средствами отладки. После того как причина поломки была устранена, можно вернуть «квадратики», обфусцировать программу и отдать ее на съедение врагам.
Помимо рассмотренных опций с помощью Windows-приложения можно также указать какие элементы должны быть переименованы, а какие должны остаться нетронутыми, то же самое касается и шифрования строк – для каждого метода, типа и сборки в отдельности можно указать, где нужно шифровать строки, а где не нужно. После того как процесс конфигурирования завершен Windows-приложение может запустить процесс обфускации на основании полученной конфигурации. При желании эту самую конфигурацию можно сохранить в конфигурационный файл. В этом случае ей можно будет воспользоваться в другой раз для запуска процесса обфускации без необходимости конфигурировать процесс еще раз.
Консольное приложение не имеет возможности конфигурировать процесс обфускации и может только использовать конфигурационный файл полученный в процессе конфигурации с помощью Windows-приложения. Для эстетов конечно, есть и тернистый путь – можно самому вручную создать или отредактировать конфигурационный файл, благо – это обыкновенный xml-файл, а  в документации Orange Heap присутствует описание его формата. Однако, мне кажется, что Windows-приложение позволяет достаточно гибко осуществлять настройку процесса обфускации и вручную править конфигурауционный файл имеет смысл только если очень чешутся руки.
В процессе обфускации Orange Heap формирует файл-отчет – в котором содержится информация о старом и новом имени переименованного элемента. В данный момент эта инфорамция из файла будет полезна при анализе и исправлении возникающих проблем в обфусцированной программе. Однако я не исключаю того, что в будущем (и только в том случае если у меня дойдут руки до реализации подобной функциональности) данный файл может быть полезен при осуществлении де-обфускации.
В данный момент Orange Heap поддерживает обфускацию WinForm-приложений. По крайней мере в процессе предрелизного тестирования я успешно и беспрепятственно смог обфусцировать несколько достаточно больших WinForm-приложений. Если какие-то вопросы с обфускаций WinForm существуют - я буду очень рад о них узнать. Пока же никто мне подобной информации не предоставил.
WPF/Silverlight – пока terra incognita для Orange Heap. Если хотите проверить - даже не пытайтесь. Все равно ничего не получится. Но я не думаю что это большая проблема. Ведь по правилам хорошего тона UI и логика должны быть разделены. Поэтому можно просто XAML поместить в одну сборку – логику в другую. В этом случае сборку с логикой можно безболезнено обфусцировать, защитив тем самым вашу интеллектуальную собственность. Собственно сам Orange Heap так обфусцирован с помощью самого себя (Windows-приложение базируется на WPF).
Хорошая новость заключается в том, что несмотря на то, что подобную ситуацию можно решить описанным выше способом, я все же планирую добавить поддержку WPF/Silverlight. Реализация этой функциональности только лишь дело времени.
Вот пожалуй и все об Orange Heap. Надеюсь данная статья вызвала интерес и вы обязательно посмотрите как он работает на деле. Orange Heap – абсолютно бесплатный, поэтому не медлите, заходите на сайт поддержки, скачивайте и пользуйтесь. И, разумеется, пишите письма. Ведь только с помощью ваших отзывов я смогу узнать о наличии проблем и попытаться их исправить, а также сделать продукт более удобным и функциональным.

среда, 28 сентября 2011 г.

Как работают обфускаторы. Дополнительные возможности


Введение

В предыдущих  двух статьях я рассказывал о том, как работают обфускаторы. Были рассмотрены методы и приемы, которые используются ими для переименования различных символов программы и для шифрования строк и ресурсов. Далее я привел простейшие примеры нелинейного преобразования кода, которые предназначены для изменения порядка следования инструкций программы. Такие преобразования делали более сложным восприятие кода и практически невозможным преобразование его к исходнму варианту. Собственно все обфускаторы как правило и отличаются качеством подобных преобразований. Чем лучше и качественнее данные преобразования осуществляются, тем выше качество защиты обфусцированного кода и соответственно больше ценится тот или иной обфускатор.
Вместе с тем, нынешние средства обфускации имеют в своем арсенале и ряд других возможностей, которые не связаны непосредственно с такими преобразованиями, а предназначены прежде всего для повышения удобства работы с инструментом и предоставления пользователю дополнительных вспомогательных функций. Как раз о таких возможностях и пойдет речь в данной статье.

Обработка зависимостей

В самом простейшем случае приложение, которое требуется защитить состоит из одного файла – единственного исполняемого exe-модуля. В данном случае при обфускации, как правило, не возникает никаких вопросов – от обфусцированного модуля никто больше не зависит, соответственно все символы (типы, поля, методы, интерфейсы, и т.д.) могут быть беспрепятственно переименованы, в том числе и те которые, имеют публичную область видимости, то есть помеченные как public.
В случае если защищаемый продукт состоит из нескольких составляющих, скажем, главного исполняемого модуля и двух-трех подключаемых dll-библиотек, ситуация немного меняется. Дело в том, что безболезненно переименовать все символы без дополнительных действий не получится. И если с приватными (private) или внутренними (internal) символами, которые используются только внутри данной сборки проблем по прежнему нету – их можно переименовывать без оглядки на всю остальную систему, то с публичными элементами такой подход неприемлем. Связано это с тем, что одна сборка, которая использует некоторые типы другой, ничего не знает об актуальном именовании типов импортируемой сборки. При связывании на этапе компиляции все имена импортируемых типов сохраняются в специальную таблицу. Позже, в процессе выполнения программы, все обращения к импортируемым типам будут осуществлятся с использованием этой таблицы. А поскольку в сборке, где эти типы объявлены, в процессе обфускации имена этих типов были переименованы, то получить информацию о типе по страму имени будет невзоможно, и в итоге мы получим исключение, информирующее о том, что запрашиваемый тип не найден.
Различные обфускаторы решают подобную проблему различными способами.
Самый простой  способ – превентивный. Это значит обфускаторы либо не позволяют изменять имена публичных символов, либо в ультимативной форме делать это крайне не рекомендуют.
Следующий способ – также предполагает решение в лоб. Суть его заключается в том, что прежде чем осуществлять переименование типов, все сборки приложения скливаются в одну. То есть вместо одного исполняемого модуля и нескольких подключаемых, получим один исполняемый модуль, который содержит все типы. После этого процесс обфускации можно проводить точно также, как это было в самом простейшем случае, когда имеется только один файл. Такой способ часто называют склеиванием зависимостей (Dependencies Merging).
Третий способ (и последний в этом обзоре) - самый корректный. Он предполагает обновление таблицы имен импортируемых символов. В данном случае после того, как все типы подключаемого модуля были переименованы, осуществляется обновление таблицы импорта исполняемого модуля, в процессе которого старые имена импортируемых символов заменяются на новые. Такой процесс преобразований получил название Inter-Assembly или Cross Assembly обфускации. Подобный вид обработки зависимостей обладает рядом преимуществ, по сравнению с предыдущими. Так, он позволяет осуществлять более глубокую обфускацию, и вместе с тем не нарушать исходную структру приложения. Это особенно актуально когда к примеру приложение состоит не из одного исполняемого файла а из двух или трех, которые совместно используют подключаемые модули. В таком случае склеивание зависимостей невозможно и стало быть рассматриваемый вид обработки зависимостей  - единственный способ обфусцировать публичные символы подключаемого модуля.

Защита от реверс-инжиниринга и модификаций

 

Защита от дизассемблирования


Несмотря на то, что в процессе обфускации структура исполняемого кода программы претерпела серьезные изменения, полученный в результате код все равно является уязвимым для несанкционированного изучения и реверс-инжиниринга. Для этого даже не нужно каких-то специализированных инструментов и знаний. Достаточно открыть исполняемый файл с помощью так называемого рефлектора или дизассемблера. Конечно же, былой очевидности исполняемого кода уже нету, однако если очень нужно исследовать некоторый алгоритм – то это не составит большого труда, Несколько часов анализа кода и цель достигнута. Для более подготовленного злоумышленника достаточно будет десятка минут.
Что бы обезопасить себя от подобных ситуаций, необходимо сделать невозможным загрузку анализируемого исполняемого модуля подобными средствами. Существуют различные варианты реализации подобного защитного механизма.
Одним из простых вариантов является вставка в тело каждого метода дополнительного фрагмента кода, который содержит некоторую некорректную или несуществующую инструкцию. При загрузке метода, у которого добавлен такой фрагмент, дизассемблер не сможет распознать эту инструкцию и соответственно не сможет загрузить тело метода для анализа злоумышленником. А для того, что бы данная инструкция не влияла на процесс выполнения программы ее следует оградить инструкциями безусловных переходов таким образом, что бы она никогда не смогла получить управление.
Более продвинутый способ может выглядеть следующим образом. Код метода шифруется с помощью некоторого алгоритма шифрования и результат шифрации помещается либо в ресурсы, либо вовсе в отдельный  файл. Исходное тело метода замещается кодом некоторого загрузчика, который знает где находится зашифрованный фрагмент кода и способ как его расшифровать. При вызове такого метода загрузчик получает управление, он дешифрует реальный код, и выполняет его. Таким образом при дезассемблировании исследовать этот код будет абсолютно невозможно.

Защита от модификаций


Некоторые обфускаторы предоставляют функциональность, которая способна защитить приложение от модификаций. Такой механизм часто называют tamper protection или tamper defense. В этом случае программа будет корректно выполнятся только в том случае если они не была изменена злоумышленником. Если какие то изменения были – программа просто перестанет работать, либо будет выводить некоторое сообщение о недопустимости использования модифицированной программы.
Для того что бы реализовать подобную функциоанльность используется подписывание (watermarking) исполняемых и подключаемых модулей. Алгоритм подписывания может быть любой. Это может быть и стандартное подписывание (signing assembly with a strong name) или же некоторый другой алгоритм, основанный на CRC или MD5. Суть такого алгоритма заключается в том, что перед выполнением программы осуществляется анализ существующего кода, в процессе которого на основании существующих инструкций кода формируется некоторое числовое значение или иными словами так называемый хэш, который впоследствии сравнивается с эталонным. Если модификаций не было, эти хеши будут идентичны, если же хеши отличаются – явный признак того, что код был модифицирован.
Кроме того подписывание, может использоваться не только для отслеживания модификации кода, но и для скрытой идентифиакции или маркировки конкретного экземпляра приложения. Подобная маркировка может помочь выявить факт несанкционированного использования приложения и определить его владельца, на который рарегистрирована покупка данного экземпляра приложения.

Способы конфигурации и пользовательский интерфейс


Немалое значение в популярности того или иного обфускатора играет удобство его использования, а также гибкость и интуитивность конфигурации процесса обфускации. Из всех вариантов конфигурирования можно выделить три основных подхода.
Первый подход предполагает наличие специального конфигурационного файла и возможности только ручного конфигурирования процесса обфускации. В этом случае придется изучать формат конфигурационного файла и вручную в текстовом редакторе осуществлять настройку процесса обфускации. Процесс достаточно сложен и утомителен, особенно в случае необходимости конфигурирования большой системы с огромным количеством различных правил.
Следующий подход основан на использовании Windows приложения для конфигурации процесса обфускации. В данном случае пользователю не нужно владеть знаниями о формате конифигурационного файла, ему достаточно указать, какие опции защиты должны быть активны и правильно настроить правила преобразований для защищаемых модулей, их типов и составных частей.
Третий вариант основан на декларативном конфигурировании защищаемого кода с помощью специальных аттрибутов. Суть данного подхода заключается в том, что на этапе разработки определенным модулям, типам и их составным частям присваиваются специальные аттрибуты, которую содержат информацию о том, как нужно обрабатывать данный элемент в процессе обфускации. Для того, что бы унифицировать процесс декларативного конфигурирования в фреймворке .NET уже существуют такие аттрибуты. К ним относятся System.Reflection.ObfuscateAssemblyAttribute и System.Reflection.ObfuscationAttribute. Первый предназначен для задания параметров конфигурации для заданной сборки, второй – для конкретного типа или его составных частей. Более подробную информацию об этих аттрибутах можно посмотреть в MSDN.
Идеальным обфускатором с точки зрения способов конифигурации процесса обфускации можно считать обфускатор, который позволяет осуществлять конфигурацию всеми тремя способами. Однако на практике такое встречается нечасто, и в большинстве случаев приходится довольствоваться меньшими возможностями.
Помимо гибкости конфигурирования процесса обфускации огромным плюсом будет наличие коммандной строки. Это позволит использовать обфускатор в автоматизированных сценариях, например при создании очередного билда или построении инсталяционного пакета.

Завершение


В данной статье мы рассмотрели существующие варианты обработки зависимостей, когда защищаемое приложение состоит из нескольких модулей, которые связаны между собой. Далее были рассмотрены наиболее распространенные варианты защиты приложения от дизассемблирования и модификаций. И наконец был дан беглый обзор возможных способов конфигурирования процесса обфускации и вариантов поставки обфускатора конечному пользвателю.
Разумеется, в статье я рассмотрел далеко не все, а лишь только самые базовые особенности, присущие большинству существующих обфускаторов. Для того что бы получить больше информации, обращайтесь к документации конкретных обфускаторов или к другим публикациям по данной тематике.

четверг, 25 августа 2011 г.

Как работают обфускаторы. Изменения внутренней логики


Введение


В прошлой статье я рассказывал о самых базовых возможностях инструментов обфускации: переименовании типов и их составных частей и шифровании строк и ресурсов программы. Такие приемы могут быть эффективными и сами по себе в простейших случаях, однако они не способны защитить секретные алгоритмы и важные части программы, поскольку весь порядок следования инструкций в коде остался такой же как и был ранее. Да, если обыкновенный неподготовленый зевака откроет обфусцированную программу рефлектором или дизассемблером, то он ничего не сможет разобрать и почти наверняка слегка растроившись прекратит свои попытки понять, как там все внутри устроено. Но если же это более подготовленый человек, которому уж очень нужно разобраться с вашим кодом и он готов потратить на это свое время, то ему не составить труда проследить порядок выполнения инструкций вашей программы.
Сегодня мы пойдем дальше и постараемся разобраться с более сложными приемами, которые своей основной целью ставят изменение логики программы. После подбных преобразований разобраться с вашим кодом будет подсилу разве что матерым проффесионалам. Да и им уже придется изрядно попотеть. К слову, защитить свою программу на 100% абсолютно ото всех – это увы, задача нереализуемая, так как еще не существует такого решения, ни для .NET, ни для неуправляемого кода, которое позволило бы полностю обезопасить прогрммные продукты от взлома и несанкционированного доступа к коду. В наше время ломается все, другой вопрос – какой ценой.

Обфускация данных


Обфускация данных подразумевает изменения структуры переменных используемых в программе а также способов их использования.
Первым вариантом обфускации данных может служить изменение видимости переменной. Так, локальная переменная используемая в коде, может быть преобразована в глобальную переменную, либо поле класса, содержащего данный метод, либо вовсе в публичное поле отдельного вспомогательного класса, который может быть сгенерирован в процессе обфускации программы. В таком случае, для того что бы проследить использование данной переменной в коде, необходимо будет потратить больше времени на определение места объявления этой переменной. Если же переменная будет представлена в виде публичного поля отдельного класса, то в процессе реверс-инжиниринга необходимо будет дополнительно осуществлять проверку где оно еще используется в пределах всей программы.
Более продвинутым методом обфускации является изменение структуры переменных. Этот метод подразумевает к примеру преобразование двумерного массива к одномерному где это допустимо и наоборот. Такие преобразования усложняют восприятие кода и затрудняют реверс инжиниринг. Также можно осуществить разбивку скалярной переменной на две или более. Допустим и обратный процесс – склеивание нескольких переменных в одну. Для большей ясности можно рассмотреть пример разбивки переменной на несколько. На листинге 1 представлен некоторый метод, который в цикле выводит значения от 1 до 10.
Листинг 1.
            char[] array = ...;
            int i = 1;
            int k = 10;
            while (i < k)
            {
              Console.WriteLine(array[i]);
              i ++;
            }

Для того что бы затруднить восприятие логики программы, достаточно перменную i разделить на две переменные i и j, каждая из которых будет инкрементироваться в своем цикле. После подобных преобразований получим код, представленный на листинге 2.
Листинг 2.
            char[] array = ...;
            int i = 0;
            int k = 5;

            while(i < 2)
            {
                int j = 1;
                while(j <= k)
                {
                    Console.WriteLine(array[i * k + j]);
                    j++;
                }
                i++;
            }

В случае использования переменных в качестве счетчика для цикла можно использовать так называемое кодирование данных, которое добавляет дополнительные инструкции для вычисления значения счетчика. Можно рассмотреть данный метод обфускации на примере. В качестве исходного кода возьмем фрагмент представленный на листинге 1, на листинге 3 – код, полученный в результате преобразований.
Листинг 3.
            char[] array = ...;
            int i=11;
            int k = 83;
            while (i < k)
            {
                Console.WriteLine(array[(i - 3) / 8]);
              i += 8;
            }

Обфускация кода

Помимо обфускации данных, существует обфускация кода или обфускация следования управляющих инструкций (control flow obfuscation). Данный вид преобразований предназначен для усложнения восприятия кода защищаемого метода. Как правило все преобразования данного типа подразумевают добавление либо инструкций переходов, предназначенных на запутывание порядка выполнения инструкций, либо всевозможных вспомогательных методов и типов, которые получают управление в процессе выполнения программы. Рассмотрим наиболее распространенные преобразования.
Добавление запутывающих ветвлений программы. Принцип данных преобразований основан на том, что в оригинальный код добавляются инструкции переходов. Рассмотрим ситуацию на примере. После осуществления преобразований кода из листинга 1 получим код, представленный на Листинге 4.
Листинг 4.
            char[] array = ...;
            int i = 1;
            int k = 10;
            while (i < k)
            {
                int j = 10 - i;

                if(j > k)
                {
                    Console.WriteLine("error");
                    i = 1;
                }
                else
                {
                    Console.WriteLine(array[k - j]);
                    i++;  
                }
            }

Если внимательно посмотреть на этот фрагмент, то можно обратить внимание, что выражение if(j > k) всегда будет ложно и фрагмент кода, относящийся к этому условию, никогда не выполнится. Такие блоки кода часто называют мертвым кодом (Dead code). Основное их предназначение заключается в отвлечении внимания, человека, занимающегося реверс-инжинирингом. Другая ветвь ничем не отличается от исходной из листинг 1, за тем исключением, что вместо i используется k j. Если посмотреть внимательно на код, то несложно прийти к выводу, что это выражение всегда будет эквивалентно i. Итак, после преобразований был получен код, имеющий на первый взгляд мало общего с исходным кодом, однако выполняющего в точности ту же самую работу.

Добавление классов-оберток для сокрытия вызовов методов стандартных типов. Даже после преобразований выполненых ранее, код по прежему остается читаемым. Причиной этому является использование метода Console.WriteLine, который переименовать как-то нету никакой возможности, так как это стандартный класс. Для того, что бы запутать код еще больше, можно создать прокси-класс, который иначе называется Dynamic Proxy. Он будет инкапсулировать в себе вызовы метода Console.WriteLine. Листинг 5 содержит реализацию класса.
Листинг 5.
    internal class llI
    {
        public static void Il(string s)
        {
            Console.WriteLine(s);
        }

        public static void Il(char c)
        {
            Console.WriteLine(c);
        }
    }

Теперь можно везде, где необходимо вызов метода Console.WriteLine заменить на вызов метода прокси llI.Il. В результате фрагмент из листинга 4, превратиться в фрагмент представленный на листинге 6.
Листинг 6.
            char[] array = ar;
            int i = 1;
            int k = 10;
            while (i < k)
            {
                int j = 10 - i;

                if(j > k)
                {
                    llI.Il("0xFF");
                    i = 1;
                }
                else
                {
                    llI.Il(array[k - j]);
                    i++;  
                }
            }

Подобные прокси отлично скрывают «настоящие» вызовы. Они могут использоваться не только со стандартными типами, но и с пользовательскими, или даже с другими прокси-объектами, создавая иерархии прокси-вызовов. Используя в комбинации приемы прокси-объектов и ветвлений можно добится невероятных результатов обфускации кода. Код полученный в результате будет очень сложен для восприятия, особенно учитывая тот факт, что названия всех методов и классов также будут в преобразованы к нечитаемому виду. Главное в этом деле не переусердствовать и вовремя остановится, так как необходимо помнить что с введением каждого нового прокси-объекта, защищаемая нами программа теряет в производительности, так как увеличивается количество избыточных вызовов и используемая глубина стека выполнения программы.

Добавление новых типов для сокрытия данных используемых в программе. Данный тип преобразований сочетает в себе обфускацию и кода и данных. Его предназначение – запутать некоторый код, который по входным данным получает в результате новые данные, которые необходимы для дальнейшего выполнения прграммы. Для ясности, рассмотрим пример, который изображен на листинге 7.
Листинг 7.
            switch(c)
            {
                case 0x00FF0000:
                    Console.WriteLine("Red Color");
                    break;
                case 0x0000FF00:
                    Console.WriteLine("Green Color");
                    break;
                case 0x000000FF:
                    Console.WriteLine("Blue Color");
                    break;
                case 0x00FFFFFF:
                    Console.WriteLine("White Color");
                    break;
            }

Этот код по числовому значению c определяет текстовое описание цвета и печатает его на консоль. Суть рассматриваемого преобразования заключается в создании нового типа, основной задачей которого будет для каждого значения входных данных ( в нашем случае это переменная c) получить соответствующие выходные данные (в нашем случае – текстовое название цвета). Реализация нового типа представлена на Листинге 8.
Листинг 8.
    internal class IIl
    {
        private static readonly uint[] _I = new uint[] { 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00FFFFFF};
        private static readonly string[] _l = new[] { "Red", "Green", "Blue", "White" };

        public static string ll(uint c)
        {
            var i = Array.IndexOf(_I, c);
            return i >= 0 ? _l[i] : string.Empty;
        }
    }

Теперь, используя данный клас можно переписать код, как показано на Листинге 9.
Листинге 9.
            var I = IIl.ll(c);

            if(!string.IsNullOrEmpty(I))
                llI.Il(I + " Color");

Обращаю внимание, что в данном примере мы воспользовались Dynamic Proxy из листинга 5 для сокрытия вызова метода Console.WriteLine. В качестве дальнейшего запутывания кода можно добавить аналогичный Dynamic Proxy для вызова метода string.IsNullOrEmpty, а также осуществить переименование всех переменных и шифтрацию строк. После этого уж наверняка будет не очевидно что на самом деле он реализует функцуиональность кода из листинга 7.

Способы отпимизации

После всех этих преобразований, код действительно становится намного запутаннее, чем это было сразу после переименования полей и шифрования строк. Что бы в нем разобраться, теперь придется изрядно потрудится. К сожалению, платой за добавленную сложность стало увеличение размера кода, которое произошло за счет добавления новых типов, дополнительных инструкций переходов и мертвого кода и падения скорости приложения ввиду добавления необходмости вызовов прокси-методов.
В целях возмещения подобных издержек в большинстве обфускаторов применяется ряд преобразований направленных на оптимизацию размера и скорости выполнения кода.
Так, в целях повышения скорости работы программы часто применяется подход, называемый запечатыванием типов. Он сводится к тому, что любой тип, который не имеет наследников помечается ключевым словом sealed. Это может способствовать повышению производительности за счет уменьшения издержек при вызове методов такого типа.
Так же, одним из способов уменьшения размера программы может служить удаление неиспользуемых фрагрментов кода. В процессе выполнения преобразований обфускатор может проанализировать всевозможные ветви выполнения кода и найти фрагменты, которые никогда не получат управление. Соответственно, они могут быть безболезненно удалены из программы. Это положительно отразится на размере программы.

Заключение

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