Распаковка Skype средствами динамической инструментализации бинарного кода. Часть 1

Intro

Skype на сегодняшний день одна из наипопулярнейших проприетарных програм с закрытым исходным кодом предоставляющая шифрованную голосовую связь и видеосвязь через Интернет между компьютерами (VoIP), использующих технологии пиринговых сетей(p2p), а также платные услуги для звонков на мобильные и стационарные телефоны. По состоянию 2011 года Skype имеет 663 миллиона пользователей по всему миру. Skype работает по принципу черного ящика. Она предоставляет очень крутые возможности абсолютно бесплатно, что конечно же заставляет задуматься над тем почему авторы ПО свободного характера кроме того что не расспространяют своих исходников, так еще и кроют его различными протекторами(виртуализаторами кода), обфусцируют код и всячески препятствуют анализу ПО. Оно и понятно - бесплатный сыр бывает только в мышеловке. В текущей статье мы попытаемся исследовать skype неординарным и новомодным образом, а именно средствами динамической инструментализации бинарного кода.

Анализ ПО

Некоторое время назад нам захотелось пощупать Skype изнутри, т.к программное обеспечение CyberSafe которое мы должны были вскоре докончить имело в своем функционале защиту пользовательских данных в Skype. Чтобы гарантированно обеспечить безопасность наших клиентов исследователями компании CyberSafe Soft были получены и проанализированы все действия данной программы. В качестве инструмента для работы мы выбрали фрэймворк динамической инструментализации бинарного кода PIN Toolkit. В данной статье ниже впервые приводится пример расспаковки и анализа зашифрованных даных которое полностью происходит в динамике. Весь процесс автоматизирован до нельзя. Под операционной системой Windows клиент Skype был разреверсен более чем основательно Ефимом Бушмановым(http://skype-open-source.blogspot.com) следствием которого стали опубликованные "исходники" известного VOIP клиента. Известно что Windows версия Skype защищена протектором кода Themida. Мы же решили проанализировать версию Skype под MacOSX надеясь на то что исполняемый файл будет менее защищенным. Но не тут то было :-)

Написание автоматического расспаковщика Skype для MacOSX

Для сборки автоматического расспаковщика Skype нам придется собрать некоторые данные для анализа. Очень подходит для этих задач интерактивный дизассемблер IDA. Для анализа вполне достаточно и Free версии. Итак пойдем вверх по ступеням. Для начала наш бинарник (исполняемый файл в формате mach-o) skype нужно будет открыть в дизассемблере и дождаться конца автоматического анализа. Вот как выглядит проанализированный код перед распаковкой, проведенный IDA Free:

Далее нужно будет поставить аппаратную точку останова(hardware breakpoint) на точку входа(EP - Entry Point) исполняемого файла. Запускаем программу на выполнение, в конечном итоге при попытке входа в точку входа во второй раз сработает наш аппаратный брэкпойнт.

Но как уже отмечалось ранее, для изучения использования средств динамической инструментализации бинарного кода при анализе мы будем использовать Intel PIN Toolkit. Для примера напишем простейшей расспаковщик(write & exec) кода Skype который будет подключаться к анализатору IDA(дизассемблер) через сервер GDB(отладчик).Впринципе такая конструкция может использовать и при написании других распаковщиков кода, потому как основа остается неизменной практически всегда.

Intel PIN

PIN Toolkit - фрэймворк разработанный корпорацией Intel для облегчения и автоматизации труда связанного с инструментализацией и анализом покрытия бинарного кода. Фрэймворк будучи написанным для платформ x86 и x86_64 позволяет инструментировать код любого приложения написанного для архитектур этих процессоров. В прошлом Intel в PIN подерживала и другие платформы как ARM/Itanium/IIRC. По весьма понятным причинам текущие платформы больше не поддерживаются. Суть инструментализации кода заключается в анализе покрытии кода при исполнении целевой программы. В свою очередь покрытие кода — мера, используемая при тестировании и анализе программного обеспечения. Она же показывает процент, насколько исходный код программы был протестирован. Данная техника покрытия кода была одной из первых методик, изобретённых для систематического тестирования и анализа программного обеспечения. Первое упоминание покрытия кода в публикациях появилось в 1963 году. Существует несколько различных способов измерения покрытия, основные из них:
  • * покрытие операторов — каждая ли строка исходного кода была выполнена и протестирована;
  • * покрытие условий — каждая ли точка решения (вычисления истинно ли или ложно выражение) была выполнена и протестирована;
  • * покрытие путей — все ли возможные пути через заданную часть кода были выполнены и протестированы;
  • * покрытие функций — каждая ли функция программы была выполнена;
  • * покрытие вход/выход — все ли вызовы функций и возвраты из них были выполнены.

Для программ с особыми требованиями к безопасности часто требуется продемонстрировать, что тестами достигается 100 % покрытие для одного из критериев.

Некоторые из приведённых критериев покрытия связаны между собой; например, покрытие путей включает в себя и покрытие условий и покрытие операторов. Структурно PIN можно разделить на основное приложение (pin), внедряемое в контекст анализируемого процесса ядро (pinvm.so) и разрабатываемый пользователем инструментальный модуль (он так же внедряется в контекст анализируемого процесса). Инструментальный модуль представляет собой динамически рассширяемую библиотеку, которая использует API, предоставляемый ядром PIN. Суть работы с API сводится к регистрации обработчиков, которые будут вызываться ядром в ходе исполнения кода исследуемого приложения и осуществлять либо логирование связанной с ходом исполнения информации, либо менять логику исполнения кода приложения любым образом, который разработчик инструментального модуля сочтет нужным. Вот пример кода взятый из сэмплов(samples) идущих в архиве с данным фрэймворком // Колличественный пример инструментализации кода // Актуальный код для инструментализации // VOID docount() { icount++; } // Код который проверяет нужность инструментализации кода в определенный момент VOID Instruction(INS ins, VOID *v) { // Вставляем инструкцию вызова PIN перед каждой исполняемой инструкцией INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END); } void Usage(void) { ... } // Инициализация компонентов PIN Toolkit int main(int argc, char * argv[]) { // Инициализация PIN if (PIN_Init(argc, argv)) return Usage(); // Регистровая инструкция "Instruction" устанавливает уровент инструментации инструкций INS_AddInstrumentFunction(Instruction, 0); // Запуск программы(без возможности возврата) PIN_StartProgram(); return 0; } В main() процедуре мы инициализируем внутренние функции PIN : установливаем уровень инструментализации INS_AddInstrumentFunction() и запускаем программу на исполнение PIN_StartProgram(). При этом для каждой последующей инструкции исследуемой программы фрэймворком PIN вызывается callback "Instruction"(отмеченный INS_AddInstrumentFunction()). В этом коллбэке мы решаем, какие инструкции хотим выбрать в текущий момент вызовом функции INS_InsertCall(). Затем, этот вызов исполняется прежде чем команда выполняется до обратного коллбэка "docount". Сообственно это и есть рабочий пример подсчитать количество исполняемых инструкций в программе.

Расспаковка Skype

Продолжение следует...

Об авторе: блог ведет Sanjar Satsura

Senior Security Analyst, Articles Writer at CyberSafe

Около 6 лет разработки программного обеспечения и реверс инженеринга закрытых программных продуктов на Windows Платформах, на *nix платформах - 4 год

Автор более 25 статей в журналах Xakep, Hakin9, "IT Sec" Magazine, "Software Developer's" Journal и 40 крупных программных продуктов и проектов.