Здесь описываются принципы приемов антиотладки, после чего приводится пример создания драйвера, который и будет обрабатывать все прерывания. Главное - не забудьте, что драйвер нужно защитить как только можно
(это главная ваша защита): не разрешить его закрывать, закрывать им все другие драйвера из ring 0
(не являющиеся системными) и т.д.

Все началось с моей попытки изучить работу дизассемблеров. Несколько взятых мною программ я посчитал уязвимыми, пример одной из них я хочу показать Вам.
Данная тема в настоящее время очень
актуальна, т.к. все программы, которые мы распространяем
как shareware или с базой паролей, могут быть так или иначе взломаны, какой бы хороший криптоалгоритм не использовали. Многие программы зависят от их защищенности, а отладчики ее снимают.

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

Отладчики делятся на несколько типов:

1) Отладчики

К ним относится такая известная программа, как SoftIce. Они используют трассировочное
прерывание Int 1, прерывание Int 3 и флаг прерывания
TF.

а) Отладчики реального режима:

Бороться с ними можно двумя путями: обнаружить отладчик или вставить в программу код, при трассировке которого отладчиком, произойдет сбой работы компьютера.

Первая возможность защиты основывается на устройстве процессоров Intel линейки 80х86: при
изменении сегментных регистров, они "теряют" трассировку одной команды:

mov ax,ss
push ax
pop ss
pushf
pop ax
pushf
pop bx
sub ax,bx
mov bx, OFFSET Otladka
mov [bx],ax

Вторая возможность основана на проверке байта в векторе Int 3. В приведенном ниже листинге мы прибавляем к 0 значение этого байта (адрес лучше
всего брать не пользуясь прерыванием int 21h). Если отладчик запущен - байт не будет равен 0.

xor ax,ax
mov es,ax
mov bx,0Ch
xor dh,dh
mov dl, BYTE PTR es:[bx]
mov bx, OFFSET Otladka
mov ax,[bx]
sub dl, 0CFh
add ax,dx
mov [bx],ax

Теперь, если где-то не 0, то вызываем функцию, обрабатывающую этот случай.

Третья: многие отладчики после 41-ого прерывания, в АХ выдают код 0F386h. (Я не знаю почему, видимо это их секрет).

mov ax,4fh
int 41h
cmp ax, 0F386
jz Otladka

Четвертая: время выполнения программы. При пропускании
команды через интерпретатор отладчика, время выполнения значительно увеличивается (в данном случае оно измеряется в тактах процессора):

rdtsc 
sub eax, ebx 
sbb edx, ecx 
rdtsc 
add eax, ebx 
adc edx, ecx 
rdtsc 
sub eax, ebx 
sbb edx, ecx 
rdtsc 
sub eax, ebx 
sbb edx, ecx 

В данном случае в EAX & EDX заносятся значения времени выполнения. Если в EDX получится 0 после выполнения этих функций - все в порядке, иначе - нет. Хочу заметить, что Intel указывает, что надо выполнять
команду rdtsc (именно она узнает о прошедшем времени) 4 раза!

б) Защищенные отладчики: 

Ищем handler отладчика по прерыванию Int 68:

mov ah,43h
int 68h
cmp ax,0F386h
jz Otladka

Существует функция IsDebuggerPresent 

BOOL IsDebuggerPresent(VOID) находится в Kernel32. Если наша программа находится под отладкой, то при вызове этой функции, она вернет 0, иначе - не 0.

Это способ отдельно для SoftIce.

За то, чтобы компании Numega позволили распространять свой продукт и "вживлять" в ядро
Windows XP, ей пришлось заплатить высокую цену - у нее постоянное имя в списке запушенных служб (на уровне 0, подробнее см. ниже), а так же постоянный ID. В VxD он 202h.

С помощью команды CreateFile, как советует автор одной из статей того же рода, что и моя, или вызовом своего драйвера, мы можем определить наличие SoftIce. Для драйвера мы должны искать \\.\NTICE (в NT) и \\.\SICE (в 98).
Если же надо узнать ее по handle в VxD, то выполняем нижеследующие операции:

mov ebx,00000202h
VxDCall VMM_Get_DDB
xchg ebx,ecx
jecxz We_didnot_find_debugger
jmp We_found_debugger

И еще один способ для защищенных отладчиков (хотя помогает и для некоторых реальных).
Drx - регистры, в которых хранятся точки останова программы (хуки). Именно в них отладчики типа SoftIce заносят данные о перехвате. Нам достаточно занести в них свои значения и если хотя бы один изменится - мы под отладчиком. 

2) Дизассемблеры

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

jmp Proc
add bx,7
dec ax
int 21
DB 0EAh
...
Proc:
...

Еще лучше, если дизассм не поймет, что до какого-то перехода дело просто не дойдет:

mox bx,0
mov ax,bx
cmp ax,0 //
условие ax=0 соблюдается всегда, но куда там глупому дизассемблеру это понять...
je go
DB 0EAh

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

3) Программы с встроенным дизассемблированием, позволяющие просматривать и изменять код

4) Обманщики и прочее

 

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

1) Проверка контрольных сумм

2) Проверка времени выполнения программ

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

Приведу небольшой пример подобной программы:

Pass Macro Reg
local з1бз2бз3
push ax
call random
cmp al,100
jn pass2
pop ax
sub ax,19
jmps ending

Pass2:
push ax
mov dx,ax
sub dx,20
dec dx
pop ax
mov ax,dx
jmps ending

Ending:
endm

Вот простой пример, который прибавляет к ax 19 разными способами.

 

Так, теперь перейдем ближе к ядру Windows! Существуют, как известно,
привилегии. Так вот, у ядра и "модулей" к нему подключенных, как известно, самые большие права. Согласитесь, что программу ядра
Windows отладить может быть куда сложнее, нежели пользовательскую программу. 

Итак, нам понадобится компилятор для ассемблера и Windows Drivers Development Kit (пакет,
помогающий создавать драйверы под Windows). мы напишем небольшой модуль, который будет следить, к примеру, за обращениями к файловой системе.
В нашу программу(пусть она будет на Delphi) допишем: 

HANDLE h = CreateFile ('\\.\antisice.vxd', 0, 0, 0, 0, \ // вызываем драйвер
FILE_FLAG_DELETE_ON_CLOSE, 0)
;

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

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии