среда, 12 сентября 2012 г.

Итоги семинара Embarcadero посвященному выпуску Delphi XE3 в Москве 12.09.2012

Сразу оговорюсь, я работаю в Delphi 2010, поэтому я шел на семинар с достаточным интересом,  ожидая увидеть значительные нововведения в ХЕ3 по сравнению с 2010. Нет, ну конечно я следил за изменениями в ХЕ и ХЕ2, но как-то так получилось, что данные продукты у меня не получилось пощупать собственноручно. Подготавливаться к семинару начал загодя. В связи с тем что в последнее время в интернете прошла череда достаточно неприятных новостей, рисующих достаточно туманную для Delphi перспективу, сразу же составил примерный список вопросов по данным пунктам. (А новости были разноплановые, от полного разгона команды разработчиков и передаче разработке в Румынию, до недавней, о запрещении разработки Client/Server на версии Professional). Так-же в список были включены вопросы о FireMonkey в связи с претензиями к ее стабильности и т.п. Часть вопросов помогли составить участники форума "Мастера Дельфи".

Вопросов получилось почти два десятка (на часть из них ответы к сожалению получить не удалось, по разным причинам) и с учетом того что на секцию вопросов отводилось в программе семинара всего 30 минут, становилось понятно, что если даже буду их задавать один я, то все равно не получится уложиться в отведенный промежуток времени, а ведь есть еще и другие участники со своим "наболевшем" :)

Изначально на семинар собиралась достаточно большая команда разработчиков с форума "Мастера Дельфи", но как-то так получилось что в итоге к отелю "Корстон", что на улице Косыгина, мы прибыли в составе "малого пехотного подразделения" всего лишь из трех человек :) Отсидев вступительную часть Джона Томаса было принято стратегическое решение, чтобы узнать необходимую информацию и не пропустить семинар, мы решили разделиться. Оставив одного из коллег в зале, со вторым я выдвинулся в сторону курилки, где и была организована засада :)


вторник, 11 сентября 2012 г.

Уход из под отладчика срывом стэка


Версия 1.0.1 

Работа программиста реализующего код защиты как правило является достаточно увлекательным занятием. Но правда сам интерес начинает проявляться только после того, как ваш продукт был в первый (или очередной) раз взломан :)
В этот момент у вас на самом деле появляется "работа", которая заключается в анализе результатов работы реверсера и построению алгоритма противодействия.
Это как война на баррикадах, сначала мы их, потом они нас и на новый круг :)

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

Немножко поделюсь и я своими наработками с обоих сторон баррикад :)
А именно способом ухода от отладчика через срыв стека.

В момент анализа стороннего ПО мы работаем как правило со отладчиком. Одним из главных элементов отладки является наличие валидного стэка отлаживаемой нити. На основании стэка нити отладчик (а если ему не под силу, то дополнительные скрипты) достаточно подробно строят стэк вызовов процедур, на базе которого происходит часть анализа кода. На стэке лежат параметры вызова функций. На том-же стэке хранятся SEH фреймы. Ни один вызов функции не обходится без стэка. Собственно стэк наше все :) (Глава 6. Базовые сведения о потоках)

В случае если вы каким либо образом обнаружили отладчик, то не стоит сразу делать TerminateProcess(), его отловят и вернут как было. Если сломать "механизм стэка", то отладка вашего приложения в большинстве случаев станет не возможна. А отловить код поломки уже на порядок сложнее, чем перехватить пресловутый Halt или TerminateProcess.
Смысл стрыва стека сводится к генерации необрабатываемого исключения PAGE_FAULT, после чего процесс можно только закрыть. Дальнейшая его отладка бесперспективна.

Здесь я приведу четыре классических варианта срыва стэка:

1. Что есть стэк с точки зрения программиста в конкретной точке кода?
Это два регистра, EBP -  Base Pointer и ESP - Stack Pointer.
Изменив эти два значения на любые произвольные, мы разрушим стэк::
  asm
    mov ebp, 0
    mov esp, 0
  end;
2. Стек всегда растет вниз, в то время как сверху остаются некие данные в том числе и адреса возврата и SEH фреймы. Все эти данные доступны для чтения и модификации. Удалив их мы разрушим стэк.

procedure _FillChar(var Dest; count: Integer; Value: Byte);
asm
{     ->EAX     Pointer to destination  }
{       EDX     count   }
{       CL      value   }
 
        PUSH    EDI
 
        MOV     EDI,EAX { Point EDI to destination              }
 
        MOV     CH,CL   { Fill EAX with value repeated 4 times  }
        MOV     EAX,ECX
        SHL     EAX,16
        MOV     AX,CX
 
        MOV     ECX,EDX
        SAR     ECX,2
        JS      @@exit
 
        REP     STOSD   { Fill count DIV 4 dwords       }
 
        MOV     ECX,EDX
        AND     ECX,3
        REP     STOSB   { Fill count MOD 4 bytes        }
 
@@exit:
        POP     EDI
end;
 
procedure TForm1.btnKillUpStackClick(Sender: TObject);
var
  P: Pointer;
begin
  _FillChar(P, MaxInt, 0);
end; 

Здесь используется реализация  FillChar из Delphi 7. В более старших версиях данная продцедура выполнена несколько другим способом и не дает необходимого эффекта.

3. Границы стека всегда обрамлены страницами с флагом PAGE_GUARD. Это можно наглядно увидеть в данном примере. Механизм работы данного флага следующий, при обращении к участку памяти с данным флагом срабатывает исключение Access Violation и данный флаг снимается. После чего при повторном обращении к этому участку срабатывает PAGE_FAULT.
В данном коде используется локальный статический массив для ускорения переполнения стека.

procedure TForm1.btnKillStackOnGuardClick(Sender: TObject);
 
  procedure T;
  var
    HugeBuff: array [0..10000] of DWORD;
  begin
    if HugeBuff[0] <> HugeBuff[10000] then
      Inc(HugeBuff[0]);
    T;
  end;
 
begin
  try
    T;
  except
    T;
  end;
end;

4. Срыв стека на рекурсивном вызове SEH фрейма. Логика данных фреймов проста, после установки они обрабатывают все исключения до тех пор, пока не будут сняты. В Delphi они представлены в частично обрезанном виде в качестве оберток try..finally/except. Идея заключается в том, что после установки SEH фрейма мы не производим его удаления и в нем-же генерируем ошибку, заставляя рекурсивно вызывать самого себя. В результате мы имеем переполнение стека, плавно перерастающее в PAGE_FAULT.

procedure TForm1.btnKillStackOnSEHClick(Sender: TObject);
begin
  asm
    lea   eax, @KillStack
    push eax
    push dword ptr [fs:0]
    mov   [fs:0], esp
    xor eax, eax
    mov eax, [eax]
  @KillStack:
    mov eax, 0
    call eax
  end;
end;

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

Подсветка кода выполнена при помощи: http://highlight.hohli.com/

среда, 5 сентября 2012 г.

Простой способ обнаружения эмуляторов ключа Guardant


При работе с ключом защиты Guardant (не важно какой модели) разработчик использует соответствующие API, при этом от него скрыт сам механизм работы с устройством, не говоря уже о протоколе обмена. Он не имеет на руках валидного хэндла устройства, пользуясь только адресом шлюза (т.н. GuardantHandle) через который идет вся работа. В случае если в системе присутствует эмулятор ключа (особенно актуально для моделей до Guardant Stealth II включительно) используя данный шлюз разработчик не сможет определить, работает ли он с реальным физическим ключом, или его эмуляцией. 

Задавшись в свое время вопросом: "как определить наличие физического ключа?", мне пришлось немного поштудировать великолепно поданный материал за авторством Павла Агурова в книге "Интерфейс USB. Практика использования и программирования". После чего потратить время на анализ вызовов API функций из трехмегабайтного объектника, линкуемого к приложению, в котором собственно и сокрыта вся "магия" работы с ключом. В итоге появилось достаточно простое решение данной проблемы не требующее использования оригинальных Guardant API. Единственный минус - все это жутко недокументированно и техническая поддержка компании Актив даже не будет рассматривать ваши вопросы, связанные с таким использованием ключей Guardant. Ну и конечно, в какой-то момент весь данный код может попросту перестать работать из-за изменений в драйверах Guardant.
Но пока что, на 5 сентября 2012 года, весь данный материал актуален и его работоспособность проверена на драйверах от версии 5.31.78, до текущей актуальной 6.00.101.


понедельник, 3 сентября 2012 г.

Теория использования электронных ключей защиты.

Версия 1.02
Security is a process, not a product. 
© Bruce Schneier


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

    Цель данной статьи - попробовать дать направление для фантазии разработчику ПО, приступившему к реализации защиты приложения с использованием ключей защиты. Впрочем, она может заинтересовать и практиков.

    По большей части все сказанное в статье было опробовано на ключах семейства Guardant, поэтому некоторые нюансы, как то предпродажная подготовка ключа, относится именно к ним. Общий-же подход к применению един для всех остальных линеек, Hasp, SenseLock, RocKey и т.п.

Что я могу сделать изначально?
Итак - у вас есть ключ и комплект разработчика (SDK).
Как правило в это-же время у вас уже есть на руках "горящий проект", который должен быть сдан уже вчера и полное отсутствие времени на изучение возможностей самого ключа. Начальство, конечно не поймет, что сам ключ еще нужно "освоить". Поэтому вам не остается ничего другого, как воспользоваться утилитами "автозащиты", поставляемыми вместе с SDK, навесив нечто, что "теоретически" воспрепятствует взломщику изучить Ваше ПО.

Данное, достаточно (скажем так) бюджетное решение, может быть оправдано только с учетом действительно жестких рамок, т.е. горящий проект и срочный релиз. Если вы будете продолжать выпускать все последующие релизы вашего ПО, полагаясь только на автозащиту, то вы совершите ошибку.

Что есть автозащита?
Автозащита является утилитой, написанной такими же разработчиками ПО, как и большинство читающих данную статью. Век искусственного интеллекта пока что не наступил и утилита автозащиты, все еще, не может заменить человека. Она не сможет распознать все тонкие места вашего ПО, которые требуют сокрытия от взломщика. Конечно, она может произвести профайлинг, с целью определения пиковой нагрузки на участки кода, виртуализация которых, существенно повлияет на скорость исполнения кода базовой программы, но не сможет определить, что именно является вашей "ноухау". Поэтому вмешательство человека, даже на уровне программ автозащиты (с целью указания критических мест посредством неких меток) все еще необходимо.
В итоге результатом работы автозащиты будет некий навесной код, который при распаковке и запуске приложения будет требовать наличие электронного ключа, в зависимости от настроек покрытия мы даже сможем поместить часть кода внутрь виртуальной машины. Из недостатков - автоматические анпакеры при наличии ключа легко все вернут в изначальное состояние.

Что есть ключ?
1. это черный ящик - все что вы храните в нем будет недоступно взломщику (с оговорками, конечно)
2. это криптография блоков данных с закрытым алгоритмом и/или ключом (шифрование/хеширование/эцп)
3. реальная привязка к железу (не нужно думать о смене процессора и материнской платы - мы привязаны к ключу)
4. возможность создания триала как по времени, так и по количеству запусков (не на всех ключах)
5. изъятие кода приложения из его состава и исполнение внутри ключа (защита от отладки данной части кода, так-же не на всех ключах)

Как подготовить ключ к работе и начать его использовать?
Прежде чем использовать ключ, нужно его подготовить к работе. Разработчику ключ передается с настройками по умолчанию. Это означает что ваш ключ отличается от демонстрационного только кодами доступа.  Для исправления данного недостатка, ключ требует перепрошивки. На этапе перепрошивки разработчик помещает в память ключа свой собственный набор алгоритмов, со своими собственными уникальными дескрипторами, свой набор защищенных ячеек и свой уникальный блок данных в открытой памяти ключа, доступный только для чтения.
Если этого не сделать, то есть большая вероятность очень быстрого создания табличного эмулятора, т.к. для его создания пригодится обычный демонстрационный ключ распространяемый бесплатно в составе SDK.

Что не нужно ждать от ключа защиты?
Большинство взломов ПО происходит из-за беспечности разработчиков, чересчур полагающихся на наличие "железа" в их схеме защиты. Не стоит делать ставку на сам ключ - он действительно просто "железка", пользуясь которой неумело, разработчик может больше навредить, чем усилить защиту проекта. Вся ваша хитрая алгоритмическая защита, на которую было потрачено полгода времени, не выдержит и получаса, если вы будете делать ошибки при использовании самого ключа.

Как не допустить ошибок при работе с ключом?
1. Не полагайтесь только на проверку наличия ключа:
После перепрошивки ключа, используя SDK, вы можете определить факт его наличия в компьютере. Но нельзя полагаться только на проверку его наличия. Очень часто встречаются программы использующие ключи защиты только на уровне "есть устройство - работаем, нет  - закрываемся". Проблема в том, что примеры эмуляции наличия устройств идут в составе DDK, поэтому обойти такую проверку не составит труда, да и в принципе (зачем подключать "тяжелую артиллерию") достаточно банального "занопливания" такой проверки в теле исполняемого файла.

2. Используйте аппаратные алгоритмы ключа:
В составе SDK, поставляемого в комплекте разработчика, есть АПИ, реализация которых выполнена полностью софтверно, т.е. без участия аппаратного ключа защиты. Такие функции, конечно гораздо более производительны, чем аппаратное преобразование через ключ, но построив защиту только с их участием, разработчик должен задуматься, а зачем ему тогда нужен собственно САМ ключ?

3. Не используйте внешнюю библиотеку:
В составе SDK поставляются объектные файлы, линкуемые к приложению и библиотека. При разработке кода защиты рекомендуется использовать только объектные файлы, т.к. если вы будете работать с ключами посредством библиотеки, взломщик, зная описание функций, сможет примерно за час написать полноценный ее аналог (шлюз) и без всяких сложностей с драйвером снимет весь ваш обмен данными с ключом прямо в UserMode.

4. Не используйте устаревшие версии аппаратных алгоритмов:
Базовый алгоритм ключа Guardant Stealth I не соответствует сегодняшним требованиям к безопасности. Для совместимости со старыми программами он все еще поддерживается. Но, если вы будете использовать его в своих приложениях, то очень скоро обнаружите в интернете полноценный эмулятор вашего продукта. Поэтому при защите приложения ориентируйтесь на алгоритм GSII64 и его вариации.

5. Думайте :)