     ОБЫЧНЫЕ ПРОБЛЕМЫ ХАКЕРА
        Проще всего разбирать чужую программу, если она уже распечатана на
    бумаге на любом языке высокого уровня (Паскаль, Си и т.д.), но в
    крайнем случае сгодится и ассемблер (машинные коды заменены их
    мнемоническим изображением). Кстати, в качестве универсального
    инструмента рекомендуем отладчик Turbo Debugger (TD), имеющий широкий
    сервис и удобный интерфейс.
         Естественно, автор программы, предусмотрев это, применил
    шифрование или ее разновидность - архивацию. А потому непосредственное
    дизассемблирование уже не даст верных результатов, если вообще что-то
    даст. Опытный хакер быстро поймет, что вместо текста идет мусор и
    сразу же начнет поиск средств для снятия шифра. Обычно он достигает
    цели, так как рано или поздно программа сама производит нужную
    операцию (она ведь не подозревает, что ее запустил хакер). Определив
    момент завершения дешифрации, можно снять в файл содержимое памяти,
    занимаемой уже нормальной программой, и, прогнав его дизассеблером,
    получить желаемый результат.
        Даже если используется поэтапная дешифровка (то есть она разнесена
    по времени), полной гарантии защиты нет - дизассемблирование лишь
    несколько затянется. Впрочем, когда дешифрацией занимается несколько
    подпрограмм и каждая является результатом работы предыдущей, хакеру
    предстоит очень нудная и кропотливая работа по их анализу.
         Отметим, что шифрование, хотя и не гарантирует полной
    безопасности программы, но вынуждает хакера запускать ее отдельные
    участки (без алгоритмов, разбираясь на ходу), таким образом,
    разработчику предоставляется возможность активно вмешиваться в процесс
    взлома (точнее, поручить это своей программе) и, в первую очередь,
    отобрать у хакера самый мощный инструмент - пошаговый режим отладки.
         В режиме отладки больше всего забот доставляет стек: его
    расположение, размер, варианты применения. Достаточно тонкое его
    использование зачастую делает невозможным даже запуск стандартных
    отладочных средств. Например, назначение в тело выполняемой задачи:
    стековый сегмент совпадает с кодовым, а указатель вершины стека SP
    указывает на саму программу. Тогда отработка отладчиком хотя бы одного
    прерывания (трассировочного) обязательно сотрет участок размером не
    менее 3-х слов. Тем более, что популярные отладчики (TD, CodView и
    другие) применяют только пользовательский стек, затирая в нашем случае
    коды на большую глубину. Кроме того, старые версии TD имеют
    принципиальную ошибку - при начальной загрузке совершенно произвольно
    уменьшают стартовое значение указателя стека на 2. Более умеренно
    работают со стеком отладчики AFD и PERISCOPE. И наиболее выгодно себя
    проявляет обычный DEBUG, поставляемый вместе с DOS.
         Переназначение стека в свободную область памяти как средство
    борьбы с назначением его в тело программы не каждому по плечу, тем
    более если через него передаются массивы данных из модуля в модуль, да
    и он сам активно участвует в работе (как, например, в пакете CONVOY
    фирмы Элиас, осуществляющем через стек разархивацию защищенного
    файла). В этом случае корректный проход программы возможен только без
    трассировки (то есть пошаговый режим исключен).
         Не менее важная и такая же сложная проблема, стоящая перед
    хакером, - отслеживание прерываний, перехватываемых исследуемой
    программой. Суть в следующем. Все стандартные отладчики для нормальной
    работы забирают первое и третье из них. Первое (трассировочное)
    используется для пошагового режима. Третье необходимо для точек
    останова программы по заданным адресам. Защитный механизм обязательно
    должен их перехватывать, чтобы предотвратить анализ под отладчиком.
    Хакер, если он разобрался с замыслом автора, может либо обойти данный
    участок (с не малой долей риска, если прерывание выполняет некоторую
    полезную функцию), либо изменить подпрограмму обработки прерывания
    таким образом, чтобы после ее отработки управление передавалось
    отладчику (но неумелое ее исправление тоже чревато...).
        Вот далеко не полный перечень того, с чем сталкивается взломщик
    в своем нелегком труде, только при добывании текста.

                                ОТПОР АНАЛИЗУ:
                                    НА УРОВНЕ ТЕКСТОВ ...
         Сформулируем приемы, мешающие анализу: шифрование и архивирование
    (как его разновидность); использование самогенерируемых кодов;
    изощренный стиль программирования и многое другое, что сможет
    придумать автор.
         Шифрование исполняемых файлов - наиболее простое средство для
    реализации. Достаточно, например, к каждому байту модуля добавить
    некоторую константу, чтобы дизассемблер ничего не понял.
        Предварительное архивирование также не представляет особых
    затруднений для хакера, но является более эффективным по сравнению с
    шифрованием, так как решает сразу две задачи: уменьшает размер
    защищаемого модуля и скрывает код от дизассемблера. Как это делается?
    Возьмем файл рисунка. Он содержит поточечное описание всех строк
    экрана (цифры - это точки определенного цвета, ноль - ее отсутствие).
    Например, рисунок красной линии на экране, состоящей из 200 точек,
    записывается в файл в виде 200 байт, а значение каждого байта равно 4
    (код для красного цвета). Архиватор заменяет перечисление одинаковых
    цифр (эти 200 четверок) элементарным шифром - двумя байтами (первый -
    количество повторов, второй - значение цвета), и размер файла резко
    уменьшается (в примере - с 200 до 2 байт). Для других типов записей
    существуют свои методы сжатия. Большинство из них описано в
    литературе, поэтому не будем останавливаться.
        Метод самогенерируемых кодов наиболее сложен в реализации, но
    очень эффективен. Суть его такова. В программу вложен массив данных,
    который сам по себе может быть исполняемым кодом (реально получающим
    управление) или смысловым текстом, но после некоторых арифметических
    или логических операций преобразуемый в участок программы, выполняющий
    иные, не менее важные функции.
         Нужно быть виртуозом программирования, чтобы заметить
    самомодифицирующуюся ловушку. Ведь при анализе листинга и с явными
    алгоритмами разбираться сложно (автор комментарии не оставляет), так
    что уж говорить о потайном смысле вроде бы расшифрованного участка.
         Впрочем, и новичок-разработчик создать такие ловушки не сможет.
    Но коль скоро он взялся за написание защитных механизмов, то ему
    придется осваивать изощренный стиль программирования, который
    запутывает дизассемблер нестандартной интерпретацией некоторых команд
    и нарушает общепринятые соглашения. Например, использование необычной
    структуры программы (совмещение стекового и кодового сегментов ).
    Интеллектуальные дизассемблеры, как правило, это плохо воспринимают. А
    перекрестные вызовы процедур, многократные переходы из модуля в модуль
    и увеличение количества точек входа в них - не позволяют выявить
    блочную структуру программы.
         Замена команд переходов, вызовов подпрограмм и прерываний
    направляет дизассемблер по ложному следу. Здесь вместо стандартного
    оператора вставляется группа других, в конечном счете выполняющих то
    же самое. Для неискушенных программистов на рисунке 4.1 (а-д)
    приведены такие варианты. Впрочем, для аналогичных эффектов достаточно
    в команде перехода изменить значение операнда (примеры е-ж). И еще
    проще модифицировать косвенные переходы (з-и).
        Кстати, и саму команду можно модифицировать. Например, на рис. 4.1
    (к) дизассемблер по адресу m: покажет PUSH AX (запись регистра AX в
    стек), поскольку этот байт имеет код 50h (01010000b), но перед ее
    выполнением один бит (4-й) меняется, в результате получается INC AX с
    кодом 40h (01000000b, увеличение содержимого регистра AX на 1) - то
    есть совсем другая команда, не отраженная в листинге.
         Кропотливая работа преобразует участок до неузнаваемости. А
    умный дизассемблер (например, Sourcer), отслеживая явную передачу
    управления (в другое место), не найдет спрятанный блок и,
    следовательно, не будет дизассемблировать его.

                              ... И В РЕЖИМЕ ОТЛАДКИ.
         Опытный хакер, если ему не надоест разбираться с защитой, будет
    неоднократно прогонять непонятые куски программы отладчиком. Некоторые
    приемы борьбы с этим уже рассматривались.
         Очень эффективное средство от пошагового выполнения программы -
    назначение стека в ее тело. А если там находятся данные для работы, то
    режим отладки усложняется. К тому же частое изменение местоположения
    стека, поверьте на слово, измотает хакера окончательно.
        Напомним, что при пошаговом режиме хакеру рекомендовалось обходить
    участки, перехватывающие 1-е прерывание. Так вот, чтобы он не смог
    воспользоваться этим советом, нужно поручить подпрограмме обработки
    прерывания некоторую полезную функцию. Например, генерацию кодов или
    дешифрацию.
        Напомним также традиционные методы защиты [1].
        Программа должна подсчитывать и проверять контрольные суммы своих
    участков для определения контрольных точек или точек останова,
    расставленных хакером. Дело в том, что стандартное применение 3-го
    прерывания предусматривает запись кода его вызова вместо байта
    программы, а это меняет контрольную сумму. Периодическая же проверка
    вовремя проинформирует программу о замене родных байт чужими, то
    есть о попытке исследования программы под отладчиком.
        Известно, что выполнение программы в режиме трассировки
    значительно уменьшает ее быстродействие. Поэтому по времени прохода
    отдельных кусков защищаемого ПО можно определить работу под
    отладчиком. По таймеру (например, запустив его 2-й канал) заранее
    просчитывают скорость выполнения некоторого участка и сравнивают его
    со значением, полученным в ходе работы программы. Если есть
    существенное расхождение в полученных результатах, то можно сделать
    вывод о выполнении данного участка под контролем. Из недостатков
    метода отметим лишь разное быстродействие процессоров на разных ПЭВМ,
    которое следует учитывать.
        Интересный способ выматывания исследователя - применение
    достаточно больших процедур, производящих некоторую сложную и, на
    первый взгляд, важную работу, но на самом деле, не имеющих никакого
    отношения к логике работы программы, так называемых пустышек. Для
    лучшей имитации их важности можно включать в них перехват 13h, 21h,
    25h и 26h прерываний (обслуживающих ввод-вывод информации на внешние
    устройства), что, безусловно, заинтересует хакера.
        Оригинальный способ защиты ПО от исследования, примененный в
    пакете COPYLOCK, использует конвейер шины данных микропроцессора. На
    рис. 4.2 изображен его фрагмент. (Надеемся, что не навлечем на себя
    гнев цивилизованных пользователей, афишируя некоторые тонкости
    программы: все равно она безнадежно устарела, да и не вскрыта лишь
    самыми ленивыми. А начинающим специалистам рекомендуем ознакомиться с
    ее работой. Полный листинг опубликован в электронном журнале НСК,
    N1, 1992 г.). Фрагмент дан со значением смещений относительно кодового
    сегмента, чтобы читатель смог увидеть, как команда REP STOSW в
    подпрограмме SUBR затирает значением из регистра AX область ОЗУ, в
    которой находится и сама подпрограмма. Тем не менее, SUBR нормально
    отрабатывает и возвращает управление в точку вызова (но только не в
    пошаговом режиме).
        Трюк очень прост: так как длина конвейера не менее 4-х байт, то,
    очевидно, команды, расположенные за REP STOSW, уже находятся в нем до
    ее выполнения, что и обеспечивает нормальную работу подпрограммы даже
    после затирания ее кода в ОЗУ. Выполнение же по одному шагу (то есть
    по трассировочному прерыванию) нарушает очередность засылки кодов в МП
    и приводит к непредсказуемому результату.
        Пример на рис. 4.3 демонстрирует более изящное использование
    конвейера. Он определяет - идет ли выполнение программы с трассировкой
    или нет, и осуществляет ветвление (команда JMP с меткой m:) в
    зависимости от этого. Здесь ветвление служит лишь для индикации работы
    под отладчиком, но вы можете применить его по своему усмотрению.
         На рис. 4.4 использование конвейера шины данных в иной
    интерпретации и в более завуалированном виде. По существу, это
    вариация на ту же тему и демонстрирует лишь разнообразие способов
    работы с конвейером.
         Аномальные явления, с которыми приходится сталкиваться при
    программировании МП Intel 80x86, не менее интересная тема при
    рассмотрении построения защитных механизмов. Информацию о них
    программист чаще всего получает экспериментальным путем (что
    становится его ноу-хау). Отступлений от стандарта обычно немного
    (исключение составляют машины фирмы Compaq с длинным перечнем
    особенностей). Об одном упоминалось в печати [2] - это потеря
    трассировочного прерывания после команд, связанных с пересылкой
    сегментных регистров типа MOV SEG.REG,R/M и POP SEG.REG. К сожалению,
    в статье результаты исследований описаны неполно. Во-первых, для МП
    8086/8088 (а точнее, японского аналога V20) существует еще один тип
    команд, заставляющий пропускать трассировочное прерывание: MOV
    R/M,SEG.REG. Во-вторых, для МП с более высоким номером также идет
    потеря трассировки, но только для стекового сегментного регистра. Это
    свойство с успехом можно применить для определения трассировки и типа
    машин.
        Известно, что отладчики при обработке 1-го прерывания анализируют
    текущую команду на PUSHF (код 9Ch) и сбрасывают Т-бит. Поэтому
    последовательность команд PUSHF, POP AX под отладчиком не позволит
    получить установленный 8-й бит в регистре AX. На рис. 4.5 представлен
    текст короткой программы, использующей эту особенность. Команда POP SS
    заставляет отладчик пропустить следующую за ней команду PUSHF из-за
    потери трассировки, и, благодаря этому, выявляется факт работы под
    отладчиком.
         Образцом знания особенностей работы МП и наиболее лаконичным
    вариантом распознавания его типа мы считаем подпрограмму, текст
    которой приведен в статье Intel insight on specific instructions
    [3]. Вот два примера из нее:
        1) Для определения типа МП, начиная с 80186 и выше, используется
    тот факт, что для них в счетчиках сдвигов (линейных и циклических)
    маскируются все биты, кроме 5-и младших, ограничивая тем самым
    величину сдвига 31 битом.
        2) Начиная с МП 80286, характерна следующая особенность: команда
    PUSH SP заносит в стек значение SP с учетом его декремента, а более
    низшие типы МП - без.
        Для пытливого ума выявление особенностей микропроцессоров сослужит
    хорошую службу как при составлении защитных алгоритмов, так и для
    идентификации ПЭВМ, а именно на таких программистов и рассчитана эта
    статья.
       В качестве примера, реализующего некоторые приемы, была разработана
    утилита DLOCK. Правила пользования ею приведены в приложении 2.

                     Л и т е р а т у р а
         1. Дмитриевский Н.Н., Расторгуев С.П.  Искусство защиты
    и раздевания программ, СОВМАРКЕТ, 1991.
         2. статья Особенности работы МП 8086/8088 в пошаговом
    режиме, Журнал д-ра Добба, N 2, 1991.
         3. Intel insight on specific instructions, Personal
    Computer World, April, 1990.

            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і         СОКРЫТИЕ АДРЕСОВ                                   і
            іа) безусловного перехода                                    і
            і    jmp m        mov ax,offset m  ; занести в стек          і
            і    . . .        push ax          ; адрес метки.            і
            і                 ret              ; перейти на метку.       і
            і                 . . .                                      і
            і  m:. . .     m: . . .                                      і
            і    . . .        . . .                                      і
            іб) вызов подпрограммы                                       і
            і    call subr          mov ax,offset m  ; занести в стек    і
            і    . . .              push ax          ; адрес возврата.   і
            і    . . .              jmp subr         ; перейти на под-   і
            і    . . .         m:   . . .            ; программу.        і
            і  subr:        subr:   . . .                                і
            ів) прерывание                                               і
            і    int 21h           pushf         ; занести в стек флаги. і
            і    . . .             xor si,si                             і
            і                      mov es,si                             і
            і                      call dword ptr es:[21h*4]             і
            і                        . . .                               і
            іг) возврат из подпрограммы                                  і
            і    . . .                  . . .          ; взять из стека  і
            і    ret                    pop bx         ; адрес возврата иі
            і                           jmp bx         ; перейти на него.і
            і   . . .                  . . .                             і
            ід) выход из прерывания                                      і
            і   iret                mov bp,sp          ; переход на точкуі
            і                       jmp dword ptr [bp] ; возврата из пре-і
            і   . . .               . . .              ; рывания.        і
            і                       add sp,4           ; точка возврата. і
            і                       popf                                 і
            і                       . . .                                і
            і        МОДИФИКАЦИЯ                                         і
            іе) перехода                                                 і
            і     mov word ptr cs:m+1,1234h  ; адрес 1234h вписать вместоі
            і     . . .                      ; 0000 у оператора безуслов-і
            і  m: jmp 0000h                  ; ного перехода             і
            і     . . .                                                  і
            іж) вызываемой подпрограммы                                  і
            і      mov word ptr cs:m+1,es    ; изменить сегмент п/п      і
            і      mov word ptr cs:m+3,5678h ; и адрес 0000 на 5678h     і
            і      . . .                                                 і
            і  m:  call far 0000h                                        і
            і      . . .                                                 і
            із) косвенного перехода                                      і
            і      mov bx,1234h                                          і
            і      jmp dword ptr cs:[bx]                                 і
            і      . . .                                                 і
            іи) косвенного вызова подпрограммы                           і
            і      les si,dword ptr cs:subr                              і
            і      call word ptr es:[si]                                 і
            і      . . .                                                 і
            ік) команды                                                  і
            і      and  byte ptr cs:m,0EFh ; обнулить 4-й бит по адресу mі
            і      . . .                                                 і
            і  m:  push ax              ; команда преобразуется в INC AX і
            і      . . .                                                 і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
                                рис. 4.1

                            Фрагмент пакета COPYLOCK
                (использование конвейера шины данных микропроцессора)
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і         . . . . .                                          і
            іcs:07FF          mov     cx,40Eh                            і
            іcs:0802          mov     di,08A6h                           і
            іcs:0805          call    subr                               і
            і        . . . . .                                           і
            і;=====================================                      і
            і; Затирание участка памяти.                                 і
            і        subr     proc    near                               і
            іcs:0D63          pushf                                      і
            іcs:0D64          cld                                        і
            іcs:0D65          mov     ax,ds                              і
            іcs:0D67          mov     es,ax                              і
            іcs:0D69          rep     stosw                              і
            іcs:0D6B          popf                                       і
            іcs:0D6C          retn                                       і
            і        subr     endp                                       і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
                                   рис. 4.2

                        ОПРЕДЕЛЕНИЕ РЕЖИМА ТРАССИРОВКИ
                                  (1 вариант)
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і 1 0000         code    segment para public                 і
            і 2              assume cs:code,ds:code                      і
            і 3                                                          і
            і 4 0000         sample1  proc                               і
            і 5                                                          і
            і 6 0000  0E               push cs                           і
            і 7 0001  1F               pop ds                            і
            і 8 0002  C6 06 0008r 00   mov byte ptr m+1,0  ; изменение   і
            і                                 ; смещения в команде JMP   і
            і 9 0007  EB 06        m:  jmp short norm_ex                 і
            і10 0009  BA 001Br         mov dx,offset trace ; выполнение сі
            і                                              ; трассировкойі
            і11 000C  EB 04 90             jmp exit                      і
            і12 000F                       norm_ex:      ; выполнение безі
            і                                            ; трассировки   і
            і13 000F  BA 0026r             mov dx,offset norm            і
            і14 0012               exit:                                 і
            і15 0012  B4 09                    mov ah,9                  і
            і16 0014  CD 21                    int 21h                   і
            і17 0016  B8 4C00              mov ax,4C00h                  і
            і18 0019  CD 21                int 21h                       і
            і19                                                          і
            і20 001B               trace   db 'Tracing!',0Ah,0Dh,'$'     і
            і21                                                          і
            і22 0026               norm    db 'Normal exit.',0Ah,0Dh,'$' і
            і23                                                          і
            і24                                                          і
            і25 0035         sample1  endp                               і
            і26 0035         code     ends                               і
            і27                       end     sample                     і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
                                   рис. 4.3

                            ОПРЕДЕЛЕНИЕ РЕЖИМА ТРАССИРОВКИ
                                     (2 вариант)
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і 1 0000        code    segment para public                  і
            і 2                     assume cs:code,ds:code               і
            і 3                                                          і
            і 4 0000        sample2    proc                              і
            і 5                                                          і
            і 6 0000  0E               push cs                           і
            і 7 0001  0E               push cs                           і
            і 8 0002  07               pop es                            і
            і 9 0003  1F               pop ds                            і
            і10 0004  BF 000Cr         mov di,offset m                   і
            і11 0007  F9                   stc                           і
            і12 0008  FC                   cld                           і
            і13 0009  B0 88                mov al,88h                    і
            і                           ; заменить код операции следующейі
            і                           ; команды на mov byte ptr m, al. і
            і14 000B  AA                   stosb                         і
            і15 000C  3A 06 000Cr  m:  cmp al,byte ptr m                 і
            і16 0010  73 06            jnc norm_ex                       і
            і17 0012  BA 0024r         mov dx,offset trace               і
            і                                ; выполнение с трассировкой,і
            і                                ; если бит CF остался       і
            і18 0015  EB 04 90         jmp exit         ; установленным. і
            і19 0018           norm_ex:                                  і
            і20 0018  BA 002Fr             mov dx,offset norm            і
            і21 001B           exit:                                     і
            і22 001B  B4 09                mov ah,9                      і
            і23 001D  CD 21                int 21h                       і
            і24 001F  B8 4C00              mov ax,4C00h                  і
            і25 0022  CD 21                int 21h                       і
            і26                                                          і
            і27 0024           trace   db 'Tracing!',0Ah,0Dh,'$'         і
            і28 0024                                                     і
            і29 002F           norm    db 'Normal exit.',0Ah,0Dh,'$'     і
            і30                                                          і
            і31                                                          і
            і32 003E        sample2  endp                                і
            і33 003E        code     ends                                і
            і34                      end     sample2                     і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
                                   рис. 4.4

                             ОПРЕДЕЛЕНИЕ РЕЖИМА ТРАССИРОВКИ
                                       (3 вариант)
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і 1 0000        code    segment para public                  і
            і 2                     assume cs:code,ds:code               і
            і 3                                                          і
            і 4 0000        sample3  proc                                і
            і 5                                                          і
            і 6 0000  0E           push cs                               і
            і 7 0001  0E           push cs                               і
            і 8 0002  1F           pop ds                                і
            і 9 0003  17           pop ss                                і
            і10 0004  9C             pushf                               і
            і11 0005  58             pop ax                              і
            і12 0006  F6 C4 01       test ah,1    ; бит TF установлен ?  і
            і13 0009  74 06          jz norm_ex   ; нет - уйти на norm_exі
            і                               ; да  - вып-ие с трассировкойі
            і14 000B  BA 001Dr       mov dx,offset trace                 і
            і15 000E  EB 04 90       jmp exit                            і
            і16 001          norm_ex:                                    і
            і17 0011  BA 0028r          mov dx,offset norm               і
            і18 0014            exit:                                    і
            і19 0014  B4 09             mov ah,9                         і
            і20 0016  CD 21             int 21h                          і
            і21 0018  B8 4C00           mov ax,4C00h                     і
            і22 001B  CD 21             int 21h                          і
            і23                                                          і
            і24 001D            trace   db 'Tracing!',0Ah,0Dh,'$'        і
            і25                                                          і
            і26 0028            norm    db 'Normal exit.',0Ah,0Dh,'$'    і
            і27                                                          і
            і28                                                          і
            і29 0037        sample3 endp                                 і
            і30 0037        code    ends                                 і
            і31                     end     sample3                      і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
                                   рис. 4.5

                            DLOCK  ver 2.0
        DLOCK.EXE - программа встраивания в .EXE файлы модуля защиты от
    отладчиков и дизассемблеров, которая сама может использоваться как
    часть защиты программного обеспечения. Ее достоинством является то,
    что пользователь выбирает место расположения встраиваемых защитных
    модулей в своей программе.
            Формат запуска программы:
            DLOCK  <имя файла> <смещение>
    где:
        <имя файла> - полное имя защищаемого .EXE файла.
        <смещение> - смещение от начала файла, заданное в любой системе
                     счисления до начала буфера , в который будет размещен
                     модуль защиты (не менее 300h байт).
        Для нормальной работы, при программировании необходимо
    зарезервировать в своей программе область памяти не менее 300h байт.
    Упростить поиск этой области в оттранслированном .EXE файле поможет
    какое-нибудь оригинальное ключевое слово. Например:
            Ассемблер:      DB      'figtebe',300h DUP (?)
            C:              char buf[0x300] = figtebe ;
        Средствами поиска любой оболочки DOS (XTREE, Norton Commander,
    PCTOOLS и др.) находим ключевое слово и определяем по нему смещение,
    требуемое в качестве 2-го аргумента формата запуска программы.
    Например, если при просмотре командой View (в режиме Hex) в пакете
    XTREE .EXE файл выглядел так:
      .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
     000240    kstackstackstack
     000250    .3.P..........!.
     000260    ....figtebe.....
     000270    ................
     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    то смещение равно 264 и формат запуска программы такой:

            DLOCK YOURFILE.EXE 0x264

                             рис. 4.6

               5. САМОМОДИФИКАЦИЯ ПРОГРАММ - ЭФФЕКТНО И ПОЛЕЗНО
         С самомодифицирующейся программой, т.е. такой программой, которая
    постоянно меняет свой исполняемый код, разбираться очень трудно.
    Представьте, в распечатке с дизассемблированным текстом хакер не понял
    какой-то участок. Естественно, что он загрузит отладчик и попробует
    прогнать непонятный блок в пошаговом режиме. Каково же будет его
    удивление, когда он не сможет найти эту часть - по тем же адресам
    записано совсем другое. Он удивится еще больше, после того как сравнит
    имеющийся листинг с тем, что выводится на экран компьютера. Ведь это
    посторонняя программа! А где же та, на анализ которой я потратил
    столько времени? - воскликнет он. И при последующих попытках
    происходит тоже самое - каждый раз предыдущий текст бесследно
    исчезает, а на его месте возникает нечто новое, требующее повторного
    анализа.
         Впрочем, полностью самомодифицирующиеся модули - большая
    редкость. В профессиональных системах защиты чаще применяется
    частичная переработка кодов. Легче всего модифицировать EXE и
    COM-файлы при загрузке в ОЗУ, выбирая куски для переработки случайным
    образом, и при этом можно сразу же вносить изменения в исходный файл
    программы на магнитном носителе. Как именно? Познакомьтесь с
    несколькими способами.

                    ЗАМЕНА НА ЭКВИВАЛЕНТЫ
         Самый простой - периодически заменять одну последовательнось
    команд на другую, внешне не похожую, но, в конечном итоге, выполняющую
    то же самое действие. Для этого подбираем эквиваленты. Например,
    команда MOV AX,BX и последовательность PUSH BX и POP AX - выполняют
    одно действие (пересылка содержимого регистра BX в регистр AX),
    команда CALL adr заменяется на последовательность PUSH IP+3 и JMP adr.
    Примерные варианты взаимозамены для основных команд ассемблера
    приведены в таблице 5.1 (естественно, для конкретных программ ее нужно
    дополнить и расширить).
         В тексте программы организуется участок, где будут храниться
    цепочки команд, с указанием адресов эквивалентных им участков. При
    очередной работе программа случайным образом меняет местами отдельные
    части из собственного тела и хранилища. В результате после каждого
    прохода исполняемый код будет случайным образом изменен до
    неузнаваемости, однако функции программы не нарушаются. Единственный
    недостаток этого способа - новый вариант исполняемого кода часто не
    может быть адекватен предыдущему по скорости работы.
         Разумеется, конкретная таблица может иметь несколько
    альтернативных вариантов для каждой последовательности. А для
    выравнивания их длин можно использовать команду NOP или ее аналоги
    (пара PUSH - POP или MOV AX,AX).
         Включение в алгоритм элементов случайности делает внешность
    задачи непредсказуемой. Пример этого способа приведен на прилагаемой
    дискете.

                             ИЗМЕНЕНИЕ КОДИРОВКИ
         Более сложный способ - модификация кодов команд с изменением
    характера выполняемых операций (рис. 5.1). Делается это так.
    Написанный на ассемблере и уже отлаженный модуль транслируется в
    объектный код с получением листинга. На листинге, не обращая внимания
    на мнемонику, ищем участки с похожими закономерностями изменения
    величин кодов команд (вот где пригодится опыт решения математических
    задач, типа найди закономерность, из популярных журналов). Затем
    выделяем найденные участки в отдельную подпрограмму и, используя
    подмеченную закономерность, составляем алгоритм ее преобразования в
    коды первого участка, второго, третьего... Этот алгоритм встраиваем
    вместо участков и по завершению преобразования подпрограммы - передаем
    ей управление. Как правило, с первой попытки полной аналогии с изъятым
    блоком не получается, поэтому нужно поманипулировать командами,
    переставить их местами, может быть - добавить лишние (тем не менее не
    нарушающими общего алгоритма), что-то заменить эквивалентной
    последовательностью.

                        ПРОХОД СВОИМ ХОДОМ ПО ДАННЫМ
         Несмотря на кажущуюся простоту, одновременное использование
    некоторых байт как операторов и операндов является высшим пилотажем
    в программировании. Способ пришел от программистов для 8-разрядных
    процессоров типа Z80, К580 и др. Дело в том, что у них основные коды
    однобайтовых пересылок и букв совпадают, к тому же у компьютеров с МП
    Z80 маловато ОЗУ (приходится его экономить). Вот и используют участки
    осмысленного текста - как для появляющихся на экране сообщений, так и
    для загрузки нужных регистров. Разумеется, некоторые буквы оказываются
    лишними при прогоне участка, как кода программы. Но, с другой стороны,
    вслед за сообщением можно поставить несколько команд, корректирующих
    результаты ненужных операций. Пример приведен в распечатке листинга
    игры Jetpac фирмы Ultimate для компьютера Spectrum-48 на рис.
    5.2 (для МП Z80). По ходу космической игры в верхней части экрана
    появляются надписи: 1UP (результат 1 игрока), HI (лучший результат
    за всю игру) и 2UP (2 игрок). При анализе текста программы
    выяснилось, что эти надписи образуют отдельную подпрограмму,
    осуществляющую пересылки некоторого значения из ячейки ОЗУ (адрес
    0D055h) в стек и из аккумулятора в эту же ячейку (то есть сложный
    обмен значениями между регистром A, стеком и ОЗУ).
         Впрочем, то что легко программируется для 8-разрядных
    процессоров, вызывает определенные трудности на 16-разрядных: и
    характер операций у буквенных кодов другой, и однобайтовых команд
    мало (с двух - трехбайтовыми разбираться еще сложнее). Тем не менее,
    после некоторой тренировки можно и этот способ взять на вооружение.

                                ЗАМЕНА БЛОКОВ
         Традиционный подход при использовании ветвления заключается в
    построении 2-х участков с разным набором команд. При выполнении
    запрограммированного условия - выполняется первый, в противном случае
    - управление передается на второй. В самомодифицирующихся модулях
    лучше отойти от общепринятой схемы, и применить переброску того или
    иного блока на один и тот же участок программы. Сработало условие - на
    следующие за ним адреса записывается первый набор команд, не сработало
    - другой, затирая предыдущие (тем более, что у МП 286 и выше - блочные
    пересылки выполняются легко и быстро).
         Определить в распечатке такой оверлейный блок - очень сложно,
    особенно, если его куски хранятся в других сегментах.

                                БЛОК ДЕКОДИРОВАНИЯ
                    (присланный Ерко В.Д., автор неизвестен):
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і    ; 1 модуль программы                                    і
            іn:  mov HL, nn   ; адрес начала блока                       і
            і    mov BC, ll   ; длина блока                              і
            і    mov AX, kk   ; ввод ключа декодера                      і
            і    xor (HL)     ; декодирование                            і
            і    mov (HL), AX ; замена закодирован. значения на раскодир.і
            і    inc HL       ; переход к очередному байту               і
            і    dec BC       ; уменьшение счетчика                      і
            і    jnz  n       ; повтор декодирования, если BC > 0        і
            і    ..........   ; продолжение модуля                       і
            і    ; 2 модуль программы                                    і
            іnn: db .......................                              і
            і                 ; (Закодированный блок.)                   і
            і    db ........  ; конец блока, адрес = nn + ll             і
            імм: db ........  ; 3 модуль (и так далее)                   і
            і                                                            і
            і      После декодирования 1  блока  и  исполнения  остальныхі
            ікоманд управление передается на декодированный блок, которыйі
            іначинается  с  такой  же  процедуры,  декодирующей следующийі
            іучасток. И таких вложений - 156.                            і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

                                    рис. 5.1

                     ИСПОЛЬЗОВАНИЕ ТЕКСТА КАК КОДА ПРОГРАММЫ
                            (для микропроцессора Z80)
            ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
            і6E1E  C3 6C 71    jmp  716Ch      ; завершение предыдущего  і
            і                                  ; участка                 і
            і6E21  47          mov  B,A        ; начало подпрограммы     і
            і6E22  31 55 D0    mov  SP,0D055h  ;1UP - текст первого      і
            і                                  ; сообщения               і
            і6E25  47          mov  B,A        ;                         і
            і6E26  32 55 D0    mov  (0D055h),A ;2UP - текст второго      і
            і                                  ; сообщения               і
            і6E29  45          mov  B,L        ;                         і
            і6E2A  48          mov  C,B        ;H - третье сообщение     і
            і6E2B  C9          ret             ;I   (оно же завершает    і
            і6E2C  21 00 00    mov  HL,0000    ;     подпрограмму)       і
            АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

                                рис. 5.2

                                                          Таблица 5.1
                                ВЗАИМОЗАМЕНЯЕМЫЕ КОМАНДЫ
                     ЪДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДї
                     і Первичный код       і Альтернативный код     і
                     ГДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і               Команды пересылки:             і
                     ГДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  mov op1,op2        і    push op2            і
                     і                     і    pop op1             і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  xchg op1,op2       і    push op1            і
                     і                     і    push op2            і
                     і                     і    pop op1             і
                     і                     і    pop op2             і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  lds r,dword ptr op і    mov r,word ptr op   і
                     і                     і    mov ds,word ptr op+2і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  les r,dword ptr op і    mov r,word ptr op   і
                     і                     і    mov es,word ptr op+2і
                     ГДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і               Арифметические команды:        і
                     ГДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  add op1,op2        і    xchg op2,ax         і
                     і                     і    add op1,ax          і
                     і                     і    xchg op2,ax         і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  adc,sub,sbb и др.  і    аналогично add      і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  inc op             і    add op,1            і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  dec op             і    sub op,1            і
                     ГДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і               Логические команды:            і
                     ГДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і and,or,xor и др.    і    аналогично add      і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  not op             і    xor op,0ff(ff)h     і
                     ГДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і               Цепочечные команды:            і
                     ГДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  rep movsb          і     push ax            і
                     і                     і m:  mov al,[si]        і
                     і                     і     mov es:[di],al     і
                     і                     і     inc si             і
                     і                     і     inc di             і
                     і                     і     loop m             і
                     і                     і     pop ax             і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  repe(repne) cmpsb  і     push ax            і
                     і                     і m:  mov al,[si]        і
                     і                     і     cmp al,es:[di]     і
                     і                     і     jne(je) m1         і
                     і                     і     inc si             і
                     і                     і     inc di             і
                     і                     і     loop m             і
                     і                     і m1: pop ax             і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  lodsb              і    mov al,[si]         і
                     і                     і    inc si              і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  stosb              і    mov es:[di],al      і
                     і                     і    inc di              і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  shift  op,cnt      і     push cx            і
                     і                     і     mov cx,cnt         і
                     і                     і m:  shift op,1         і
                     і                     і     loop m             і
                     і                     і     pop cx             і
                     ГДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і         Команды передачи управления:         і
                     ГДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  j(условие) loc     і     jn(условие) loc1   і
                     і                     і     jmp loc            і
                     і                     і loc1:  . . .           і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  loop loc           і    dec cx              і
                     і                     і    jne loc             і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  jmp addr           і    push addr           і
                     і                     і    ret                 і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  jmp dword ptr addr і    push addr+2         і
                     і                     і    push addr           і
                     і                     і    retf                і
                     ГДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДґ
                     і  call addr          і     push m             і
                     і                     і     jmp addr           і
                     і                     і m:   . . .             і
                     АДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДЩ
         Cокращения: op, op1, op2 - операнды команд; r - операнд-регистр;
    shift - код команды сдвига; cnt - счетчик в командах сдвига; loc -
    метка в командах перехода и цикла; addr - адрес в командах перехода и
    вызова подпрограмм.
вверх