Примеры реальных взломов

Вид материалаДокументы

Содержание


внутри обработчика
Листинг 72 описание структуры CONTEXT. в квадратных скобках приведены смещения ее элементов (интересующие нас регистры залиты ра
001B:004466AD SUB EBP,EAX ; дефективный anti-debug trick
001B:004466BA MOV EDI,ESI ; старая регистрационная запись
Листинг 74 инициализация регистров перед передачей управления (различные смысловые группы команд залиты "своим" цветом)
001B:004466f8 mov fs:[ecx],edi
Подобный материал:
1   ...   5   6   7   8   9   10   11   12   13

внутри обработчика


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


EXCEPTION_DISPOSITION __cdecl _except_handler

(

struct _EXCEPTION_RECORD *ExceptionRecord, // [ 0x04]

void * EstablisherFrame, // [ 0x08]

struct _CONTEXT *ContextRecord, // [ 0x0C]

void * DispatcherContext // [ 0x10]

);

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

Теперь становится понятным смысл инструкции "MOV EAX, [ESP + 0C]", загружающей указатель на контекст прерванного исключением потока. Следующая за ней машинная команда "LEA ESI, [EAX + A4h]" устанавливает регистр ESI на что-то, хранящаяся в контексте по смещению A4h, но как узнать: что именно? Для этого нам потребуется обратится к описанию самой структуры _CONTEXT, содержащейся в файле NTDDK.h:


typedef struct _CONTEXT

{

ULONG ContextFlags; // [0x00]

ULONG Dr0; // [0x04]

ULONG Dr1; // [0x08]

ULONG Dr2; // [0x0C]

ULONG Dr3; // [0x10]

ULONG Dr6; // [0x14]

ULONG Dr7; // [0x18]

typedef struct _FLOATING_SAVE_AREA

{

ULONG ControlWord; // [0x1C]

ULONG StatusWord; // [0x20]

ULONG TagWord; // [0x24]

ULONG ErrorOffset; // [0x28]

ULONG ErrorSelector; // [0x2C]

ULONG DataOffset; // [0x30]

ULONG DataSelector; // [0x34]

UCHAR RegisterArea[SIZE_OF_80387_REGISTERS]; // [0x38]

ULONG Cr0NpxState; // [0x88]

} FLOATING_SAVE_AREA;

ULONG SegGs; // [0x8C]

ULONG SegFs; // [0x90]

ULONG SegEs; // [0x94]

ULONG SegDs; // [0x98]

ULONG Edi; // [0x9C]

ULONG Esi; // [0xA0]

ULONG Ebx; // [0xA4]

ULONG Edx; // [0xA8]

ULONG Ecx; // [0xAC]

ULONG Eax; // [0xB0]

ULONG Ebp; // [0xB4]

ULONG Eip; // [0xB8]

ULONG SegCs; // MUST BE SANITIZED // [0xBC]

ULONG EFlags; // MUST BE SANITIZED // [0xC0]

ULONG Esp; // [0xC4]

ULONG SegSs; // [0xC8]

UCHAR ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; // [0xCC]

} CONTEXT;

Листинг 72 описание структуры CONTEXT. в квадратных скобках приведены смещения ее элементов (интересующие нас регистры залиты различными цветами)

Ага, "LEA ESI, [EAX + A4h]" направляет указатель ESI на "контекстное" значение регистра EBX (помните, он явно инициализировался перед возбуждением исключения?). Соответственно, LODSD читает это самое значение и помещает его в регистр EAX, а чуть позже пересылает обратно в EBX (см. машинную команду "MOV EBX, EAX") так что чемпион интуиции типа Мессинга мог бы догадаться до значение поля A4h и без анализа структуры контекста.

Следующая команда LODSD извлекает из контекста очередной по счету регистр, – EDX. И в EDX же его и пересылает. А что у нас содержится в EDX? Сейчас минуточку… (прокручиваем экран дизассемблера назад или перелистываем принтерные распечатки в живописном беспорядке разбросанные на нашем хакерском столе) так, понятно, здесь хранился адрес предыдущего обработчика структурных исключений.

После этого машинную команду "MOV EBP, [ESI + 0Сh]" уже можно и не анализировать, постольку и так очевидно, что она вытаскивает из контекста значение регистра EBP. Но все-таки, давайте проверим! Так, ESI у нас указывает на регистр ECX, а на 0Ch/4 = 3 (три) двойных слова ниже расположен… черт возьми, EBP расположен на два регистра ниже ECX, а здесь находится EIP! Вот тебе бабушка и интуиция на Юрьев день!!! Хорошо, а на что тогда указывает "MOV ESI, [ESI +18]"? Считаем: 18h/4 = 6. А на шесть двойных слов ниже ECX проживает регистр ESP. Каков поворот событий, а? Следующая команда LODSD загружает отнюдь не само значение регистра ESP (как она делала до этого), а содержимое двойного слова, на которое указывает ESP! Остается выяснить, что же у нас находилось на верхушке стека в момент вызова исключения. Нет, отнюдь не адрес возврата из прерывания (обработка исключений в Widows осуществляется совсем не так, как в MS-DOS), а адрес возврата из функции CALL 446752, который ввиду ее "географического положения" совпадает с целевым адресом вызываемой функции, то есть непосредственно 446752h на вершине стека и есть.

А теперь, внимание на экран! Машинная команда "SUB EBP, EAX" (SUB _IP, [_SP]), осуществляющая контроль значения регистра EIP на момент возникновения исключения, представляет собой псевдоантиотладочныйкод, который лишь демонстрирует принципиальную возможность обнаружения отладчика типа soft-ice, но реально не использует ее, поскольку, как мы и говорили выше, содержимое EIP на момент возбуждения исключения никак не зависит от природы этого самого исключения. Вот если бы команды CALL и RETF были бы разделены хотя бы одним NOP, – вот тогда прием, используемый Хароном, сработал бы на все сто, а так… это либо дефект защиты, либо своеобразная шутка ее разработчика.


001B:00446692 XOR ECX,ECX ; ECX := 0

001B:00446694 MOV EAX,[ESP+0C] ; EAX := &_CONTEXT

001B:00446698 LEA ESI,[EAX+000000A4] ; ESI := &_CONTEXT.EBX

001B:0044669E LODSD ; EAX := _CONTEXT.EBX

001B:0044669F MOV EBX,EAX ; EBX := _EBX

001B:004466A1 LODSD ; EAX := _CONTEXT.EDX

001B:004466A2 BSWAP EAX ; "шифруемся"

001B:004466A4 MOV EDX,EAX ; EDX := _EDX

001B:004466A6 MOV EBP,[ESI+0C] ; EBP := _CONTEXT.EIP

001B:004466A9 MOV ESI,[ESI+18] ; ESI := _CONTEXT.ESP

001B:004466AC LODSD ; EAX := [_CONTEXT.ESP]

001B:004466AD SUB EBP,EAX ; дефективный anti-debug trick

001B:004466AF JZ 004466B9 ; jump always

001B:004466B1 MOV EDI,FS:[ECX] ; мертвый код

001B:004466B4 CALL 004466DE ; мертвый код

Листинг 73 загрузка регистров из контекста (залита светло-серым цветом), дефективный anti-debug trick (залит черным цветом) и мертвый код (залит темно-серым цветом)

Если проверка на присутствие отладчика прошла успешно (то есть, отладчик отсутствует или не обнаружен), защита увеличивает значение регистра EAX на единицу (а в нем, как мы помним, находится адрес 446752h, занесенный туда машинной командой CALL 446752) и пересылает его в ячейку памяти расположенную по адресу [ESI], предварительно уменьшив ESI на четыре. То есть, фактически делает просто INC [_CONTEXT.ESP].

Затем еще раз уменьшает ESI на четыре и записывает по новому адресу константу 18460h, что эквивалентно PUSH_IN_CONTEXT_STACK 18460h. Тем временем, в EAX засылается значение на которое указывает EBX (441С93h для справки), а сам EBX увеличивается на четыре, становясь в конечном счете равным 4460C4h.

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


001B:004466B9 INC EAX

001B:004466BA MOV EDI,ESI ; старая регистрационная запись

001B:004466BC SUB ESI,04

001B:004466BF MOV [ESI],EAX

001B:004466C1 SUB ESI,04

001B:004466C4 MOV DWORD PTR [ESI],18460

001B:004466CA MOV EAX,[EBX]

001B:004466CC ADD EBX,04 ; EBX := 4460C4h

001B:004466CF OR EDX,EDX

001B:004466D1 JNZ 004466D9

001B:004466D3 MOV [EDX+000178CF],DL

001B:004466D9 CALL 004466F6

Листинг 74 инициализация регистров перед передачей управления (различные смысловые группы команд залиты "своим" цветом)

После увлечения из стека адреса возврата 446752h, оголилась прежняя регистрационная запись обработчика структурного исключения, которая вновь принудительно заносится в поле FS:[00h] (в приведенном ниже листинге соответствующая команда выделена жирным шрифтом) Спрашивается на кой такой хрен? А вот и не хрен это, а раскрутка стека обработчиков структурных исключений. Операционная система подспудно пытается передать обработку исключения следующему обработчику в цепочке, что естественно, не входит наши планы и мы переустанавливаем наш обработчик заново.


001B:004466F6 MOV ESP,ESI

001B:004466F8 MOV FS:[ECX],EDI

001B:004466FB POP EBP

001B:004466FC RET ; выход!

Листинг 75 переустановка обработчика структурных исключений