Гошко С.В., Анализ защиты программ и рекомендации по её усилению, Системный Администратор, октябрь 2003, стр.78-84
             Анализ защиты программ и рекомендации по её усилению

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

В большинстве своём крэкеры используют следующи инструментарий:

1) Отладчики (Soft Ice, TRW, Turbo Debugger, ...)

2) Дизассемберы (IDA, W32 Dasm, Sourcer, ... )

3) Шестнадцетиричные редакторы (Hiew, Hex Workshop, ...)

4) Мониторы (FileMon, RegMon, ...)

Теперь рассмотрим, как в основном взламываются обычные программы на конкретных
примерах.

;******************************************************************************;

;------------------------------------------------------------------------------;
;                            Winzip 8.0                                        ;
;------------------------------------------------------------------------------;

Загрузить Soft ice поставить точку прерывания на GetDlgitemTexta
затем заполнить поля формы Winzip'a (name,serial_number; заполнить 
чем угодно) и нажать OK появится окно Softice там поставить точку
прерывания на 0167:00407AAB и посмотреть данные.

0) bpx GetDlgItemTextA
1) bpx 0167:00407AAB
2) d eax
3) В окне данных будет серийный номер сгенерированный по вашему
   имени.

;------------------------------------------------------------------------------;
;                           Hexworkshop 2.54                                   ;
;------------------------------------------------------------------------------;

Ставим точку прерывания на GetWindowTexta затем ставим точку
прерывания по адрессу 0167:004262AF трассируем без вхождения (F10)
до второго перехода jnz этот второй переход выполняться не хочет
изменяем флаг нуля (в окне регистров наводим на Z и нажимаем INS)
Теперь убираем все точки прерывания bc * и закрываем Softice - F5
Вводим имя и фирму на которую хотим зарегистрироваться вот и всё.

;------------------------------------------------------------------------------;
;                                Winamp 2.xx                                   ;
;------------------------------------------------------------------------------;

0) bpx hmemcpy
1) Вводим serial
2) всплываем в Softice
3) видим что-то вроде: cmp eax,esi два раза клацаем на эту строку
   жмём F5
4) ? eax (там и хранится регистрационный код) 
5) Убираем точки прерывания выключаем Softice

;------------------------------------------------------------------------------;
;                                Irfanview 2.0                                 ;
;------------------------------------------------------------------------------;

Данная программа была препарирована за 5 минут а делалось это так:

0) Устанавливаем точку прерывания в Softice на MessageBoxA:

  bpx MessageBoxA   

1) Всплывя в Irfan мы видим кучу разных сравнений и несколько 
переходов моё внимание привлекла функция Kernel32'a - заполни 
форму я нашёл на неё переход и увидел что переход находится по 
адресу: 

0167:00440347 jnz 00440375

3)Исправляем флаг нуля в окне регистров и жмём F5 вуаля мы 
зарегистрированы

P.S. Можно было в HIEW заменить этот переход на jz.

;------------------------------------------------------------------------------;
;                               mIRC v. 6.01                                   ;
;------------------------------------------------------------------------------;

Загрузить Soft Ice, поставить точку прерывания на MessageBoxA.

0) bpx MessageBoxA

Затем заполнить поля формы и нажать "ОК". Появится окно Soft Ice.
Выйдем из User32.dll и затем проанализируем код предшествующий
сообщению об ошибке.

Выше вызова MessageBoxA я натолкнулся на следующий вызов фнкции:

..........................
push 0056d737            ; Помещается в стэк смещение серийного номера
push 0056d350            ; Помещается в стэк смещение имени
call 004c37b1            ; Вызов функции, которая проверяет корректность
test eax,eax             ; Проверка результата функции
jz   004c3ce1            ; !!!!!!!!!!!!!
..........................

Команду условного перехода необходимо либо обратить (jnz 004c3ce1), либо
забить nop'ами.
Адрес инструкции условного перехода следующий: 004с3с24

;------------------------------------------------------------------------------;
;                                Customiser                                    ;
;------------------------------------------------------------------------------;

Данная программа работает определённое количество времени и потом сообщает о 
том, что время её использования кончилось. И больше работать не хочет - это
выражается в том, что пропадает кнопка "Continue".

Начнём наше исследование с того, что взглянём какие функции данная программа
импортирует. Нас особо будут интересовать функции из USER32.dll так как диалог 
на котором была кнопка "Continue" импортируется скорее всего оттуда. Мое 
внимание привлекла функция - DialogBoxParamA. 

Загружаем Soft Ice и ставим брейкпоинт на эту функцию:

0) bpx DialogBoxParamA

После этого запускаем программу и сразу же всплывёт Soft Ice в недрах USER32.dll
и мы поднимемся из этой функции (нажатием F12). 

Протрассируем до адреса 41f8c4 и там мы заметим подозрительный условный переход
следом за которым идёт ещё один. Второй переход указывает на вызов функции 
EnableWindow. Становится ясно, что нам нужно перейти по этому переходу.
Для этого мы инвертируем флаг нуля в Soft Ice и нажимаем F5, что возвращает
управление программе и мы видим, что программа заработала.

Теперь мы должны пропатчить её. Запускаем HIEW переходим по адресу 41f8c4 и
меняем байты "75 03" на "EB 18".

После этого снимем брейкпоинт в Soft Ice.

1) bc *

Запустим программу нажмём кнопку "Exit" и программа нет не завершится, а 
наоборот запустится. Но по прежнему будет постоянно появляться это мерзкое
окно с сообщением, что время твое прошло. Поэтому мы попытаемся избавиться от
него. Найдём вызов функции рисования данного окна получится адрес 41f89c и там
будет вызов подпрограммы из 3-х байт, которые перезапишим 3-мя nop'ами ("90").
Перезаписывать будем при помощи HIEW.

           Вот полная информация о проведённых изменениях:

41f89c: 90 90 90
41f8c4: EB 18

После этого программа будет запускаться безо всяких окон.

Существует ещё один способ продления лицензии без изменения программы. Нужно
перед её установкой установить дату в системе лет на 20 вперёд, а после 
установки вернуть старую дату. 

;------------------------------------------------------------------------------;
;                          Shadow Security Scanner 5.37                        ;
;------------------------------------------------------------------------------;

Эта программа один из лучших сетевых сканеров безопасности. Она имеет 
ограничение по времени на использование (trial). 
Метод, который применялся при взломе Customiser не работатет потому, что
окно создаётся функцией CretaeWindowExA, это всё несколько усложняет.
Но можно пойти другим путём. Трассировать программу без вхождения в подпрограммы
пока не появится окно уведомляющее что время программы закончилось.
Нужно запомнить адрес подпрограммы в которой вывоится данное сообщение и 
заглянуть туда. Там мы можем заметить какое-то странное сравнение:

cmp [ebx+14],0 

После этого переход если не равно нулю. Поиграем с флагом нуля и что мы видим
программа заработала осталось только её пропатчить:

593598: EB

Вот и всё, защита с программы снята.

;******************************************************************************;

Как мы можем видеть взлом программы чаще всего отталкивается от перехвата 
WIN API функций. Как можно этому противостоять?

            Существует несколько методов:

(I) Написание и использование своих функций с переходом куда нибудь в
   средину WIN API функции

Рассмотрим написание своих функций на примере функции GetWindowTextA.
Как устанавливается прерывания отладчика на имя функции? Вставляется байт
"CC" перед первой инструкцией функции. Но мы ведь можем сделать так, что на
этот первый байт иструкции управление никогда и не будет передано.
Вы спросите как это сделать? 

        Давайте рассмотрим как начинается данная функция:

;------------------------------------------------------------------------------;
77d5c13a:   push    0c
77d5c13c:   push    77d6e498
77d5c141:   call    77d439c0
................................
;------------------------------------------------------------------------------; 

Так начало функции выглядит у меня. Каждому необходимо посмотреть на её 
начало под отладчиком самому:

1) Загрузить Soft Ice

2) Ctrl + D ( Всплываем в Soft Ice)

3) u GetWindowTextA

Теперь давайте разберёмся как выглядит вызов нашей функции в программе в общем
виде:

;------------------------------------------------------------------------------;
        push    xxxxxxxx
        ................
        push    xxxxxxxx
        call    GetWindowTextA
;------------------------------------------------------------------------------;

Причём при вызове GetWindowTextA идёт переход по адресу 77d5c13a. Мы же можем 
сделать следующим образом:

;------------------------------------------------------------------------------;
        push    xxxxxxxx
        ................
        push    xxxxxxxx
        push    return_address
        push    0c
        jmp     (GetWindowTextA+2)
;------------------------------------------------------------------------------;

Данный метод позволяет защититься от точки прерывания в Soft Ice такого вида:

bpx GetWindowTextA

Так же можно сделать следующим образом:

;------------------------------------------------------------------------------;
        push    xxxxxxxx
        ................
        push    xxxxxxxx
        push    return_address
        push    0c
        push    77d6e498
        jmp     (GetWindowTextA+7)
;------------------------------------------------------------------------------;

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

Это позволит ещё более обезопасить программу от перехвата WIN API функций.

(II) Подсчёт контрольной суммы WIN API функций 

Данный метод мной ещё нигде не встречался и хочется верить, что его идея
принадлежит мне.

Он заключается в том, что перед вызовом необходимой WIN API функции 
подсчитывается её контрольная сумма и сверяется с той суммой которая хранится в
программе. Как вы помните при установленной точке прерывания на место первой
инструкции записывает байт "СС" (int 3), который и изменяет контрольную сумму
данной функции. При проверке контрольной суммы данной функции и определении
отладки можно выйти из программы. 

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

(III) Обнаружение отладчика и завершение программы

Все отладчики под системы семейства Windows делятся по уровням:

    - ring 3 ( Уровень приложения )
    - ring 0 ( Уровень ядра )

В основном все отладчики кроме Soft Ice относятся к отладчикам уровня 
приложения и против них существует множество методов, которые мы сейчас и 
рассмотрим:

1) Использование WIN API функции IsDebuggerPresent

            Использовать её довольно легко:

//----------------------------------------------------------------------------//
        if (!IsDebuggerPresent()) goto no_debugger
        //........................................
no_debugger:
//----------------------------------------------------------------------------//

                 Или вот её полный код:
;------------------------------------------------------------------------------;
            mov     eax,fs:[018h]
            mov     eax,[eax+30h]
            movzx   eax,byte ptr [eax+02]
            ret
;------------------------------------------------------------------------------;
                             
2) Погибель отладчику несут операции с SEH'ом

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

Вначале нам необходимо сохранить оригинальный обработчик SEH:

;------------------------------------------------------------------------------;
        push    dword ptr fs:[0]
;------------------------------------------------------------------------------;

Теперь нам необходимо заменить оригинальный обработчик на свой:

;------------------------------------------------------------------------------;
        push    offset SEH_Handler
        mov     fs:[0],esp
;------------------------------------------------------------------------------;

Восстановление оригинального обработчика в нашем случае будет производться
следующей инструкцией:

;------------------------------------------------------------------------------;
        pop     dword ptr fs:[0]
;------------------------------------------------------------------------------;

Данная инструкция восстанавливает из стэка оригинальный обработчик SEH.

А теперь рассмотрим программу которая обрушивает отладчик, а без отладчика
работает:

;------------------------------------------------------------------------------;
.386p
.model  flat                            

extrn           ExitProcess:PROC

.data
Hi		dd  0
.code
;------------------------------------------------------------------------------;
start:
	    pop     ebx                  ; Адрес для вызова исключения
            call    setupSEH             ;                                       
                                                                              
Ex_Handler:
            mov     esp,[esp+8]          ; Ошибка дает нам старый ESP      
                                         ; в [ESP+8]
exit:
            push    0                    ; Кладём в стэк 0
            call    ExitProcess          ; И завершаем программу  

;------------------------------------------------------------------------------;
setupSEH:
            push    dword ptr fs:[0]     ; Push'им оригинальный 
                                         ; обработчик SEH
            mov     fs:[0],esp           ; И помещаем новый (который
                                         ; находится после первого 
                                         ; call)
    
                                         ; Пытаемся писать в ядро (что
            mov     eax,012345678h       ; вызовет исключение)
            xchg    eax,[ebx]

end start
;------------------------------------------------------------------------------;

После того, как мы научились противодействовать отладчикам уровня приложения не
мешало бы научиться бороться с Soft Ice'ом.

Победить правильно настроенный Soft Ice практически не возможно, но дело в том,
что в 90% он не настроен должным образом.

1) Стандартным методом является использование следующего кода:

;------------------------------------------------------------------------------;
        push    ss
        pop     ss
        int     3
;------------------------------------------------------------------------------;
При попадания на инструкцию int 3 взломщик попадёт внутрь обработчика и будет
там вдали от защитного механизма программы.

2) Следующий код эффективно использовать для модификации ключа расшифровки:

;------------------------------------------------------------------------------;
        mov     ebp,"BCHK"
        push    ss
        pop     ss
        int     3
        db      blabla      ; Опкод модифицирующий ключ декриптования
;------------------------------------------------------------------------------;

3) Разработчики Soft Ice оставили возможности для определения его присутствия, 
   которыми мы и воспользуемся для защиты своей программы от Soft Ice.

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

                Список файлов:

    - "\\.\NTICE"
    - "\\.\SIWVIDSTART"
    - "\\.\SICE"
    - "\\.\SIWVID"

Ну вот с отладчиками и закончили. 

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

                Рассмотрим листинг:
                                
;------------------------------------------------------------------------------;
        mov eax,04ebh
        jmp $-4
next:
;------------------------------------------------------------------------------;

Что же делает выделенная восклицательными знаками часть кода?

1) В eax помещается значение 04ebh(это опкод команды jmp $+4)

2) jmp $-4, переходит на значение 04ebh

3) jmp $+4, переходит на метку next

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

А теперь последняя часть данного текста: защита от изменения кода.
Многие программисты видели так называемые "патчи"( заплатки ) для программ,
которые сводят суть защиты программы на нет. Как же с этим бороться ?

            Существует два метода защиты:

1) Шифровать при помощи контрольной суммы критического места программы важные
   данные

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

2) Использование помехозащищённого программирования

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

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

Надеюсь данный текст поможет вам достойно ответить на вызов крэкеров.

;-------------------------------------------------------------[slon (2003)]----;