Главная страница
Навигация по странице:

  • 2.1. Методы противодействия отладчикам

  • 2.1.1. Защита от отладчиков реального режима

  • 2.1.2. Защита от отладчиков защищенного режима

  • 2.1.3. Методы, основанные на невозможности полного эмулирования отладчиком среды загрузки программы

  • 2.2. Методы противодействия дизассемблированию программного обеспечения

  • 2.3. Защита, основанная на человеческом факторе злоумышленника

  • Лекции. Введение. Защита программного обеспечения от несанкционированного использования с помощью программноаппаратных средств


    Скачать 4.72 Mb.
    НазваниеВведение. Защита программного обеспечения от несанкционированного использования с помощью программноаппаратных средств
    АнкорЛекции
    Дата19.11.2022
    Размер4.72 Mb.
    Формат файлаdoc
    Имя файлаLektsii_PASOIB.doc
    ТипДокументы
    #797594
    страница5 из 12
    1   2   3   4   5   6   7   8   9   ...   12
    Глава 2. Методы противодействия обратному проектированию
    2.1. Методы противодействия отладчикам
    Таким образом, задача злоумышленника во многом упрощается после локализации им модуля защиты в коде программы. Один из подходов, используемых разработчиками для защиты от взлома – не дать взломщику локализовать данный модуль или во многом затруднить ему эту задачу. Так как решение данной задачи во многом облегчается с помощью средств отладки, то многие разработчики ПО пытаются противостоять данным средствам и реализовать различные защиты против средств отладки. Различают два типа защит против отладочных средств – защиты против отладчиков реального режима и защита против отладчиков защищенного режима.

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

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

    2. Определение наличия отладчика в оперативной памяти используя различные «дырки», допущенные при реализации отладчиков, либо внедренные разработчиком отладчика принудительно.

    Использование недокументированных команд и возможностей процессора.

    Использование того, что некоторые отладчики при загрузке отлаживаемой программы не могут полностью эмулировать «чистую» среду ее запуска в ОС (например, обнуляют некоторые регистры, которые могут нести определенный смысл).

    Рассмотрим более подробно реализации защит против отладчиков реального и защищенного режимов.
    2.1.1. Защита от отладчиков реального режима

    Работа данных отладчиков построена на использовании двух аппаратных прерываний: int 1, с помощью которого в отладчике выполняется пошаговое исполнение программы и int 3, предназначенное для вставок точек останова в код отлаживаемой программы. При трассировке программы отладчиком задействуется регистр флагов, а именно флаг трассировки TF.

    Трик 1

    Данная ловушка основана на том, что вследствие аппаратной особенности реализации процессора INTEL, после исполнения команды pop ss первое прерывание не может быть вызвано, и отладчик «не замечает» и пропускает следующую за данной командой инструкцию. Механизм защиты в данном случае может выглядеть следующим образом.

    ……




    Push ss;




    Pop ss;

    Защита

    Pushf;

    Кладем в стек значение флагового регистра. Данную команду отладчик не замечает и не исполняет

    Pop ax;

    Восстанавливаем ax из стека. В случае наличия отладчика в памяти, мы извлечем не флаговый регистр

    Test ax,100h;

    Проверка флага на трассировку. Подразумевается, что если мы под отладчиком, то проверка не пройдет

    Jnz DebuggerDetected;

    Переход на процедуру завершения работы

    ……




    Трик 2

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

    Механизм защиты может выглядеть в данном случае следующим образом.

    ……




    Xor dx,dx;

    Обнуляем регистр dx

    Mov ds,dx;

    Обнуляем регистр ds

    Mov ax, 2501h;




    Int 21h;

    Функция 25, подфункция 1. Вектор обработчика первого прерывания устанавливается на адрес 0000:0000

    ……




    Существуют также механизмы противодействия установке точек останова в отладчике.

    Точка останова используются отладчиком следующим образом.

    В код программы вставляется вместо данной точки код 0xCC, а значение памяти, которое находилось по его адресу, запоминается отладчиком.

    Когда программа встречает команду с кодом 0xCC, она вызывает исключительную ситуацию 0x3h. При этом в стеке запоминается регистр флагов, указатель текущего кодового сегмента (CS), указатель команд IP, запрещаются прерывания (очищается флаг FI) и сбрасывается флаг трассировки.

    После выполнение действий п.2, программа поступает в монопольное распоряжение процессора.

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

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

    Используя данный факт, для защиты от установки точек останова можно воспользоваться следующими методами.

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

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

    3. Зашифровать код программы на ключе, который занести в стек, читая его по команде pop посимвольно. В данном случае, при вмешательстве отладчика код программы будет расшифрован неверно.

    Трик 3 Аппаратное запрещение прерываний

    Данная ловушка основана на том, что запрещение прерываний от клавиатуры приводит к зависанию отладчиков реального режима. Если войти в режим отладки, то невозможно будет набрать ни одну команду отладчика, в том числе и выйти из него. Запретить прерывания от клавиатуры в реальном режиме MS-DOS можно следующими способами.

    Способ 1

    ……

    In al, 21h

    Or al, 00000010b

    Out 21h,al
    Способ 2

    In al, 61

    Or al, 10000000b

    Out 61h, al
    Способ 3

    Mov al, 0Adh

    Out 64h,al

    Трик 4. Перепрограммирование видеоадаптера

    Многие отладчики реального режима перестают корректно работать, если запретить видеовывод через прерывание int 10h.
    2.1.2. Защита от отладчиков защищенного режима

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

    Кроме этого, особенностью защищенного режима является введение специализированных отладочных регистров DR0 – DR7, предназначенных для отладочных целей (таких как установка точек останова на обращение к определенным адресам памяти и портам).

    Отладчик защищенного режима запускается с нулевым уровнем привилегий CPL (Code Privelege Level), и полностью защищен от возможных воздействий со стороны отлаживаемой программы, которую он запускает с более низким уровнем привилегий. И взламываемая программа, при правильной реализации отладчика, уже не может «отравить» жизнь отладчику, "подпортив" регистры. Однако, в данном случае возможно определить наличие отладчика в оперативной памяти. При этом могут быть использованы следующие методы.

    1. Один из методов основан на том, что при наличии отладчика в памяти, сама программа не может получить доступа к регистрам отладки – доступ к ним запрещен. Этот тип защиты можно отнести как к обнаружению отладчика в оперативной памяти, так и к невозможности полного эмулирования чистой среды загрузки программы.

    ……




    Lea ebx, continue;

    Заносим в ebx адрес перехода

    Mov dr0,ebx;

    Записываем адрес перехода в dr0

    Xor eax,ebx;

    Исключаем вероятность совпадения eax и ebx

    Mov eax, dr0;

    Читаем адрес перехода из dr0

    Jmp ax;

    Переходим по данному адресу. Если доступ к регистру dr0 запрещен, то мы перейдем не по тому адресу и ход выполнения программы будет нарушен

    ……




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

    Трик 1. Шифрование кода программы с расшифровкой через отладочный регистр.

    ……




    Mov eax,11111111h;

    Ключ, которым зашифрован код программы

    Mov dr0,eax;

    Сохраним ключ в отладочном регистре

    Xor eax,0СВСВСВСВh xor 11111111h;

    Кладем мусор в eax

    Mov eax,dr0;

    Возвращаем из регистра отладки ключ в eax

    Xor dword ptr cs:[hiddencode],eax;

    Расшифровываем код

    ……




    hiddencode:

    [зашифрованный код]

    Трик 2. Основан на том, что отладчики защищенного режима теряют одно трассировочное прерывание при установке DRx.

    Это позволяет использовать трик, аналогичный трику 1 отладчиков реального режима (потеря трассировочного прерывания по команде pop ss).
    2.1.3. Методы, основанные на невозможности полного эмулирования отладчиком среды загрузки программы

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

    Одной из таких дырок «страдает» наиболее распространенный отладчик защищенного режима SoftIce.

    Пример определения SoftIce из отлаживаемой программы.

    ……




    Mov bx,202h




    Mov ax,1684h




    Xor di,di




    Int 21h;

    Если функция не определена, то di останется неизменным

    Or di,di




    Jnz SoftIceDectected

    У SoftIce данная функция занята

    ……




    Одной из уязвимостей отладчика Turbo Debuger является то, что он перехватывает прерывание int 00 (Деление на 0), выставляет свой адрес обработки, и передает управление на соответствующий код ядра отладчика. В данном случае, защита в программе может быть реализована следующим образом.

    ……




    Start:







    Mov ax, offset Continue




    Mov dx,cs




    Xor bx,bx




    Mov es,bx




    Cli




    Xchg ax,es:[bx]




    Xchg dx,es:[bx+2]




    Sti




    Div bx

    Done:

    mov ax,4C00h




    Int 21h

    Continue:

    Cli




    Xchg ax, es:[bx]




    Xchg dx, es[bx+2]




    Sti




    Mov ah,9




    Mov dx, offsetMsg




    Int 21h




    Jmp Done

    ……




    Многие отладчики, также обнуляют некоторые регистры, которые могут нести определенный смысл. Например, при нормальном запуске (из под операционной системы MS-DOS) AX и BX отражают правильность аргументов в командной строке. DS=DX, SI=IP, DI=SP. Равенство соответствующих регистров друг другу можно проверить.

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

    ……

    Pop bx

    Pop eax

    Push eax

    Push bx

    Ret

    ……

    Ниже приведены дополнительные особенности, позволяющие выявить в памяти некоторые отладчики.

    1. Отладчики Turbo Debugger, CodeView.

    Используют стек отлаживаемой программы.

    Используют int 1 и int 3 для трассировки.

    Перехват прерываний int 0,1,3 и FP инструкций.

    Некорректная работа с видеобуфером.

    Некорректное выставление начальных значений регистров.

    Неправильное дизассемблирование инструкций вида JMP $+1.

    2. Отладчик SoftIce – обнаруживается по присутствию VxD устройства WinIce. Точку входа в SoftIce можно получить при вызове функции int 2F с параметрами: AX=1648h, BX=0202h (WinIce VxD ID), ES:DI=0. Результат возвращается в ES:DI.

    3. Отладчик DegLucker

    Переключение в нестандартный видеорежим.

    Невозможность перехвата портов ввода/вывода.

    Запирание клавиатуры через i/o портов 60h/64h.

    Предоставляет API через int 15h функции 0FFxxh. Трассирует программу через DRx (аппаратные точки останова).

    Кроме этого, в программных продуктах могут быть использованы следующие средства, позволяющие затруднить их отладку.

    1. Использование прерываний int 1 и int 3 для собственных нужд.

    2. Неоднократная смена стека программы.

    3. Переход на свои подпрограммы из обработчиков прерываний.

    4. Передача информации через буфер клавиатуры.

    5. Расшифровка программы через стек (хранение в нем пароля).

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

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

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

    Трики, как правило, включаются в программу по правилу «чем больше тем лучше» и от их исключения, логика работы программы не меняется. В некоторых случаях, совокупный размер кодов триков сравним с размером программы.
    2.2. Методы противодействия дизассемблированию программного обеспечения

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

    Выделяют несколько общих подходов к защите ПО от дизассемблирования.

    1. Шифрование кода. Защищаемый участок кода шифруется каким-либо алгоритмом, а в программу добавляется модуль расшифровки, который в нужный момент расшифровывает его и передает ему управление. В данном случае, защищаемый участок кода перед дизассемблером предстанет в зашифрованном виде, и будет воспринят дизассемблером неверно. Перед злоумышленником в данном случае предстанет просто «мусор».

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

    2. Самомодификация кода программой. В данном случае, средства статического исследования ПО также не смогут верно воспринять участки кода, модифицируемые в процессе работы программы, ей самой. Следует отметить, что самомодифицироваться программы могут только при работе процессора реальном режиме (не в защищенном).

    3. Различные ходы, приводящие к обману дизассемблера. Этот способ заключается в том, чтобы с помощью различных «хитрых» ходов запутать дизассемблер, подсунув данные вместо кода, или дизориентировать его логику, повести его по ложному следу. В качестве примеров такой защиты можно привести следующие участки кода.

    Пример 2.1.
    00000000: B83534 Mov ax, 03435; “45”

    00000000: EBFC jmps 00000001

    ……дальнейший код.
    После выполнения данных инструкций, дальнейший код программы, воспринятый дизассемблером будет совершенно неверен., так как программа перейдет на инструкцию 3534EB, что соответствует команде xor ax, 0EB34h. На самом же деле программой выполняется следующий код.

    Mov ax,03435

    Xor ax,0EB34 <- инструкция дизассемблером пропущена.

    …… дальнейший код.

    Пример 2.2.

    Labellone:

    Mov ax, 0CEBh




    Jmp labellone+1




    Db 1,2,3,4,5,6,7,8,9,0




    Mov ax,1234h




    Ret

    Данная защита аналогична примеру 2.1.

    Существуют и чисто психологические способы защиты, которые сбивают с толку не дизассемблер, а человека, анализирующего текст программы после дизассемблирования. Например, обмен векторов прерываний 21h и 10h, или обращение к порту 378h как к 8378h.

    4. Сокрытие команд передачи управления.

    Сокрытие команд передачи управления приводит к тому, что дизассемблер не может построить граф передачи управления. Например, можно модифицировать адреса переходов в ходе выполнения программы (только для реального режима).

    Пример 2.3.

    Mov word ptr cs:m[1],1234h;

    модификация адреса перехода

    ……




    m: jmp place

    вместо метки place ранее внесен другой адрес

    ……





    Пример 2.4.

    mov word ptr cs:m[1],es;

    модификация адреса вызываемой подпрограммы

    mov word ptr cs:m[3],5678h




    ……




    m: CALL far 0000h

    данный адрес перехода был ранее модифицирован

    ……




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

    5. Использование методики косвенной передачи управления также затрудняет анализ дизассемблированного кода.

    Mov bx,1234h

    Jmp dword ptr cs:[bx]

    6. Использование нестандартных способов передачи управления.

    В данном случае, для затруднения изучения дизассемблированного кода, разработчиком ПО могут использоваться нестандартные способы передачи управления по каким либо адресам. Разработчик может, например, моделировать работу операторов JMP, CALL, INT … средствами других операторов, что затрудним понимание листинга на языке ассемблера. Примеры возможных альтернативных записей команд передачи управления приведены в таблице 2.1.

    Таблица 2.1. Примеры возможных альтернативных записей команд передачи управления

    Первичный код

    Альтернативный код



    jmp m



    m:



    Mov ax, offset m

    Push ax

    Ret



    m:



    call subr

    m:



    subr:



    ret

    Mov ax, offset m

    Push ax

    Jmp subr

    m:



    subr:



    ret



    int 13h



    pushf; флаги в стек

    push cs

    mov ax, offset m

    push ax; занести в стек адрес возврата

    xor ax,ax

    mov es,ax

    jmp dword ptr es:[13h*4]

    m:






    ret



    pop bx

    jmp bx

    Iret

    Mov bp,sp

    Jmp dword ptr [bp]; переход на точку возврата из прерывания



    add sp, 4; точка возврата

    popf

    7. Использование ссылок вида [EAX+0ffffff64h] (в качестве эквивалента [EAX-9Ch]) с целью затруднения анализа дизассемблированных текстов человеком.

    8. Можно сделать короткий переход вперед, а между командой перехода и адресом перехода встроить мусор. В данном случае, можно добиться совершенно неверного результата дизассемблирования.
    2.3. Защита, основанная на человеческом факторе злоумышленника

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

    1. Перемешивание кода программы.

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

    Пример 2.5. Пусть оригинальный код выглядит следующим образом:

    start:

    Инструкция 1

    Инструкция 2

    Инструкция 3

    Инструкция 4

    end:

    Тогда, данный код может быть, например, перемешан следующим образом:

    start:

    jmp @@1

    @@4:

    instruction 4

    jmp end

    @@2:

    instruction 2

    jmp @@3

    @@1:

    instruction 1

    jmp @@2

    @@3:

    instruction 3

    jmp @@4

    end:

    Допустим, что в тексте программы находится типичный кусок какой-либо классической защиты, например,

    0000000: 16 push ss

    0000001: 17 pop ss

    0000002: 9C pushf

    0000003: 58 pop ax

    0000004: A90001 test ax

    0000007: 740C je DebuggerDetected

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

    00000000: 16 push ss

    00000001: EB07 jmps 00000000A

    00000003: 58 pop ax

    00000004: EB08 jmps 00000000E

    00000006: 740B je RealModeDebuggerDetected

    00000008: EB09 jmps 000000013

    0000000A: 17 pop ss

    0000000B: 9C pushf

    0000000C: EBF5 jmps 000000003

    0000000E: A96400 test ax,00064

    00000011: EBF3 jmps 000000006

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

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

    2. Задача анализа кода взломщиком намного усложнится, если в некоторых случаях команду безусловного перехода заменить на команду условного (в данном случае, мы должны быть точно уверены в содержимом регистра флагов). Например, в следующем случае, переход будет осуществляться всегда:

    Xor ax,ax

    Je 000000025
    1   2   3   4   5   6   7   8   9   ...   12


    написать администратору сайта