Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #57, АВГУСТ 2005 г.

Ключик к сердцу

Chingachguk/HI-TECH

Спецвыпуск: Хакер, номер #057, стр. 057-094-7


Также драйвер обычно пытается всеми доступными ему способами скрыть все свои алгоритмы, а в особенности - нижние процедуры типа Driver_EncryptData, содержащие внутренние криптографические алгоритмы и сам обмен с ключом. Обмен с ключом может быть реализован непосредственно в драйвере (например LPT-реализация ключа) или же обычными системными драйверами (в случае с USB-вариантом работать с ключом напрямую через порты ввода-вывода и управлять DMA было бы крайне нерационально: пришлось бы размещать внутри драйвера защиты функциональность, достаточную для совместимости с различными спецификациями USB-шины и т.п.). Собственно, обмен напрямую с LPT возможен только лишь потому, что он уже полностью стабилен и прост в реализации, но "не на бумаге" все оказалось сложнее: ключ был спроектирован как "прозрачный" для обмена с принтером, тем не менее были случаи сбоев в работе принтера, разделяющего этот ресурс.

Защита драйвера также может носить "параноидальный" характер, но при этом обладающий своими особенностями: акцент в ней сделан на защиту не от отладки, а от дизассемблирования (те вытаскивания критичных алгоритмов), и, в случае реализации обмена с ключом непосредственно в драйвере, защищается обмен: это скрытие от перехвата команд in/out.

Рассмотрим подробнее эти два аспекта защиты. В защите от дизассемблирования может применяться так называемая техника "обфускации". Исходный код драйвера, написанный, допустим, на С, транслируется в ассемблерный код:

void Driver_EncryptData(void *EntryDataAboveOrEqualThan4Bytes, DWORD DataLen)

{

...

KeyStruct->Field7= MAG_CONST_FOR_FIELD7;

}

// После трансляции в ассемблер:

; KeyStruct->Field7= MAG_CONST_FOR_FIELD7;

mov dword ptr [ebx].Field7, 0xABCDEF77;

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

// После "обфускатора":

;; KeyStruct->Field7= MAG_CONST_FOR_FIELD7; // СИ

; mov dword ptr [ebx].Field7, 0xABCDEF77; // ASM first variant

push const1

push eax

moveax,const2

jmp@Label1

...

@Label1:

xor[esp+8],eax

push ebx

movebx,const3

jmp@Label2

...

@Label2:

sub[esp+0Ch],ebx

popebx

popeax

jmp@Label3

...

@Label3:

popdword ptr [ebx].Field7

В результате в поле KeyStruct->Field7 попадает значение (const1^const2)-const3, причем все константы подобраны таким образом, что в результате (const1^const2)-const3 == MAG_CONST_FOR_FIELD7. Понятно, что такой код читать намного сложнее, чем одну ассемблерную команду. Данная техника крайне многообразна: например, критичные поля могут разбиваться на части, за ними следует множество команд типа pushfd/popfd или xchg ebp,ebp, ничего полезного не делающих, однако выполнение алгоритма от этого не изменяется.

Тонкости кухни

Теперь о защите от перехвата обмена с ключом на самом нижнем уровне. Если в драйвере используются команды in/out, они охраняются от перехвата всеми возможными способами. Почему? Если реверсер может эмулировать ответы ключа или просто запомнить их на уровне in/out-команд, то, перехватив работу защиты с портами, он сможет удобным для себя способом разместить свой эмулирующий код сразу после драйвера защиты перед отсутствующим ключом. Перехват in/out в NT-системах на уровне ядра, то есть при котором перехватывающий и перехватываемый код находятся в равном положении, возможен только (это, скорее, тема отдельного разговора) через регистры отладки DRx. Если бы драйвер защиты находился на меньшем уровне привилегий или OS Windows использовала бы механизм вложенных задач (Nested Task), то было бы возможно также использовать битовую карту ввода-вывода - permission i/o access map. Защитить код от мониторинга портов ввода-вывода через регистры DRx в этом случае возможно только сбросом бита DE в регистре CR4 процессора, что может с успехом применить защита. Сброс же регистров отладки DRx, нередко используемый в защитах, имеет фатальный недостаток: доступ к этим регистрам также может быть перехвачен через те же регистры DRx, и код перехватчика полностью прозрачен для защиты. А недостаток этого способа эмуляции (он же - преимущество для защиты) состоит в необходимости для такого эмулятора разделять общие ресурсы с отладчиком - прерывание int1, которое может сильно затруднить отладку эмулятора или его использование.

Назад на стр. 057-094-6  Содержание  Вперед на стр. 057-094-8