Техника защиты компакт-дисков от копирования

         

первое вхождение искомой последовательности в образе диска


Листинг 8.2. Второе вхождение искомой последовательности в образе диска

00010370:  01 00 00 00 00 01 91

63 ¦ CD 36 00 00 36 CD 63 67   O    OСc=6  6=cg

00010380:  06 1D 17 0D 0A 28 0C 00 ¦ 00 00 01 00 00 01 32 00   ¦-¦d0(+   O  O2

00010390:  30 00 31 00 20 00 2D 00 ¦ 20 00 50 00 65 00 72 00   0 1   -   P e r

000103A0:  73 00 6F 00 6E 00 61 00 ¦ 6C 00 20 00 4A 00 65 00   s o n a l   J e

000103B0:  73 00 75 00 73 00 2E 00 ¦ 6D 00 70 00 33 00 3B 00   s u s . m p 3 ;

Листинг 70 второе вхождение искомой последовательности в образе диска

Искомое значение действительно присутствует в образе, причем не в одном, а в двух… нет, даже в четырех экземплярах! Нет, это не чертовщина — – все так и должно быть. Современные лазерные диски содержат две файловых системы: одна из которых —– ISO-9660 —– записывается на диск исключительно для его совместимости с устаревшим программным обеспечением, ограничивающим максимальную длину файла одиннадцатью символами (восемь из которых приходятся собственно на само имя, а оставшиеся три —– на расширение). Современное программное обеспечение работает с более "продвинутыми" файловыми системами к которым принадлежит и файловая система Joliet Джульета (ДжульеттаJoliet), разработанная компанией Microsoft. Говорите, "только Ромео для полноты компании не хватает"? А ведь файловая система Romeo Ромео (РомеоRomeo) действительно есть и разработана она компанией Adaptec. К сожалению, этот "Ромео" не получил большого распространения и скоропостижно "скончался", так что Joliet (Джульетта) вынуждена оставаться лась в одиночестве.

Но довольно романтики, возвращаемся к делу. Заботиться о синхронизации обоих файловых систем в общем-то и необязательно, т. к. операционная система Windows "видит" только Joliet Джульету и игнорирует ISO-9660, а MS-DOS поступает с точностью до наоборот. Поэтому, если мы увеличим длину файла в JolietДжульете, но "забудем" внести соответствующее изменения в ISO-9660 (а некоторые разработчики защит именно так и поступают!), Windows не заподозрит в этом и тени обмана.
Вот хакеры —– другое дело! Оригинальные длины файлов, оставленные в ISO-9660, значительно упрощают задачу взломщика и потому оставлять их там ни в коем случае не стоит! К тому же, существуют драйвера, позволяющие вручную выбирать какую из имеющихся файловых систем следует монтировать. Так что не будем лениться и скорректируем оба значения сразу, изменив два старших байта с "36 00" на "FF 66" (естественно, вы можете предпочесть и другое значение). Когда будете это делать обратите внимание на двойное слово "00 36 CD 63" —– это тоже длина файла, но записанная в "противоестественном" для IBM PC порядке. Здесь младший байт располагается по большему адресу. Адрес стартового сектора файла так же записан в двух вариантах. Очевидно, такая схема представления информации выбрана по соображениям переносимости и каждая платформа вправе выбирать наиболее естественный для нее порядок байтов, однако, не факт, что операционная система Windows выберет вариант "младший байт по меньшему адресу". Все решает файловый драйвер, а он в зависимости от особенностей реализации может работать с любым из этих полей. Поэтому, оба этих поля всегда должны быть согласованы.

Теперь исправленный (в смысле искаженный) ISO-образ можно смело записывать на диск CD-R/CD-RW диск или смонтировать образ на виртуальный CD-привод (для этого вам понадобиться Alcohol 120% или его аналоги). Даем команду "DIR" и смотрим (листинг 8.3).:

Листинг 8.3. Размер файла Personal Jesus.mp3 умышленно искажен

>
dir N:\Depeche Mode

Том в устройстве N имеет метку 030706_2038



 Серийный номер тома: 61A1-A7EE

 Содержимое папки N:\Depeche Mode

06.07.2003  21:56       <DIR>
          .

06.07.2003  21:56       <DIR>
          ..

01.01.1601  04:00        1 728 040 291

01 - Personal Jesus.mp3

30.06.2003  00:11            3 574 805 02 - See You.mp3



30.06.2003  00:12            3 472 405 03 - Strangerlove.mp3

30.06.2003  00:12            3 718 165 04 - Enjoy The Silence.mp3

30.06.2003  00:13            2 956 643 05 - The Meaning Of Love.mp3

30.06.2003  00:14            3 820 565 06 - Master and Servant.mp3

30.06.2003  00:15            3 066 149 07 - Never Let Me Down Again.mp3

30.06.2003  00:16            3 806 772 08 - Its Called a Heart.mp3

30.06.2003  00:16            3 813 460 09 - Little 15.mp3

30.06.2003  00:17            3 574 805 10 - Everything Counts.mp3

30.06.2003  00:18            3 687 236 11 - People Are People.mp3

30.06.2003  00:19            4 916 036 12 - The Thing You Said.mp3

30.06.2003  00:20            4 182 100 13 - Agent Orange.mp3

30.06.2003  00:21            4 585 012 14 - World in my Eyes.mp3

30.06.2003  00:22            3 646 276 15 - Behind The Wheel.mp3

30.06.2003  00:22            3 049 012 16 - Black Celebration (live).mp3

30.06.2003  00:23            3 800 085 17 - Nothing.mp3

30.06.2003  00:25            7 151 700 18 - Bonus (unnamed).mp3

              18 файлов  1 794 861 517 байт

               2 папок               0 байт свободно

Листинг 71 размер файла "Personal Jesus.mp3" умышленно искажен путем

Вот это да! Размер файла увеличился до 1 .728 .040 .291 байт (см. выделенную полужирным шрифтом строку листинга), что более чем в два с половиной раза превышает объем всего лазерного диска. А еще говорят, что часть не может быть больше целого! Естественно, попытка скопировать файл на жесткий диск винчестер заканчивается провалом и приходится искать обходные пути. Будем исходить из того, что файлы на диске располагаются последовательно, т. е. за последним сектором одного файла, непосредственно следует стартовый сектор следующего. А, поскольку, стартовые сектора всех файлов нам известны, определение номеров последних секторов для всех файлов, за исключением самого последнего, не составит никакого труда.



Скопируем ISO-образ защищенного диска в файл и рассмотрим его каталог еще раз (листинг 8.4).:

Листинг 8.4. Подследственный фрагмент "препарируемого" образа файла

0000E040:  00 01 01 01 54 00 94 01 ¦ 00 00 00 00 01 91 63 CD    OOOT Ф¦    OСc=

0000E050:  FF 66 66 FF CD 63 00 00 ¦ 00 00 00 00 00 00 00 00    ff =c

0000E060:  01 00 00 01 32 00 30 00 ¦ 31 00 20 00 2D 00 20 00   O  O2 0 1   -

0000E070:  50 00 65 00 72 00 73 00 ¦ 6F 00 6E 00 61 00 6C 00   P e r s o n a l

0000E080:  20 00 4A 00 65 00 73 00 ¦ 75 00 73 00 2E 00 6D 00     J e s u s . m

0000E090:  70 00 33 00 3B 00 31 00 ¦ 46 00 6B 08 00 00 00 00   p 3 ; 1 F k•

0000E0A0:  08 6B 15 8C 99 00 00 99 ¦ 8C 15 67 06 1D 17 0B 1C   •k§МЩ  ЩМ§g¦-¦>L

0000E0B0:  0C 00 00 00 01 00 00 01 ¦ 24 00 30 00 32 00 20 00   +   O  O$ 0 2

0000E0C0:  2D 00 20 00 53 00 65 00 ¦ 65 00 20 00 59 00 6F 00   -   S e e   Y o

0000E0D0:  75 00 2E 00 6D 00 70 00 ¦ 33 00 3B 00 31 00 50 00   u . m p 3 ; 1 P

Листинг 72 подследственный фрагмент препарируемого образа файла

Наименьший номер стартового сектора файла, следующий за сектором 0191h, равен 086Bh. Таким образом, файл "01  – – Personal Jesus.mp3" не может содержать более 086Bh -? 0191h === 6DAh секторов или 1754 * 2048 === 3 .592 .192  байт. Конечно, это несколько завышенная оценка и действительный размер файла на 1,5полтора  Ккилобайта короче, но такое расхождение уже не критично. Большинство мультимедийных файлов будут вполне нормально обрабатываться даже при наличии некоторого количества постороннего "мусора" на "хвосте". Исправив образ файла, запишем его на диск или просто усечем файл до необходимых размеров с помощью любой подручной утилиты типа "Добермана Пинчера" ("Pinch of File").

А что делать, если вас не устраивает столь низкая стойкость подобной защиты? Ну… кое-что вы можете сделать.


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

Защитный механизм, "знающий" на сколько секторов сдвинуто действительное смещение файла относительно его начала, должен либо переместить файловый указатель вызовом функции SetFilePointer, либо "проглотить" "мусорные" данные с помощью функции ReadFile. Оба способа практически равнозначны и каждый из них имеет свои сильные и слабые стороны. Функция SetFilePointer работает значительно быстрее, но слишком заметена (для хакеров), напротив, с вызовом функции ReadFile еще предстоит разобраться какие данные он читает —– значимые или нет.

Рассмотрим, как выглядит процесс взлома на практике. Поскольку, писать полноценный MP3-плейер мне было откровенного лениво (да и места он бы занял немеряно), то вся обработка данных сводится к выводу оригинального содержимого файла на экран (листинг 8.5). Перед первым запуском программы, стартовый сектор защищенного файла должен быть уменьшен на величину _NSEC_, а размер увеличен по меньшей мере на 2048*_NSEC_ байт, верхнего же ограничения на максимальную длину нет (все 32-бита поля длины — ваши).

Листинг 8.5. [crackme.27AF7A2Dh] Демонстрация обработки файлов с искаженными атрибутами стартового сектора и длины

/*----------------------------------------------------------------------------

 *

 *                                              crack me 27AF7A2D

 *                                              =================

 *

 *      демонстрационный  пример   обработки  файлов  с  умышленно  уменьшенным номером



 * номером стартового сектора и увеличенной длиной; позиционирование файлового

 * указателя осуществляется вызовом функции fseek, потому этот  crackme  очень

 * очень легко взломать (см. так же ..... - как более стойкий пример реализации

 * реализации той же защиты)

 *

 * Build 0x001 @ 02.07.2003

----------------------------------------------------------------------------*/

#include <stdio.h>

// настройки программы

// ===================

// имя открываемого файла (если защита находится на CD, то полный путь к файлу

// очевидно указывать не обязательно)

#define _FN_                              "M:\\Depeche Mode\\01 - Personal Jesus.mp3"

// количество секторов на которые смещено начало файла

#define _NSEC_                          4

// оригинальный размер файла

#define _FSIZ_                          3591523

// размер пользовательской части сектора

#define SECTOR_SIZE                     2048

// ширина экрана в символах (нужна для вывода дампа)

#define _SCREEN_LEN_           80

// размер обрабатываемого блока

#define BLOCK_SIZE               0x666

// поиск минимума двух чисел

#define _MIN(a,b)  ((a<b)?a:b)

// ВЫВОД HEX-ДАМПА НА ЭКРАН

//-------------------------

// src - указатель на выводимые данные

// n   - кол-во выводимых на экран байт

print_hex(unsigned char *src, int n)

{

             int a; static p = 1;

             for (a=1; a <= n; a++)

                      printf("%02x%s",src[a-1],(p++%(_SCREEN_LEN_/3-1))?" ":"\n");

}

main()

{

             int  a;

             FILE *f;

             long p = _FSIZ_;

             char buf[BLOCK_SIZE];

            

             // TITLE

             fprintf(stderr, "crackme 27af7a2d by Kris Kaspersky\n");

            

             // пытаемся открыть файл

             if ((f = fopen(_FN_, "rb")) == 0)

             {

                      fprintf(stderr, "-ERR: can not open %s\n",_FN_);
return -1;



             }

            

             // пропускам _NSEC_ лишних секторов, находящихся в начале файле

             fseek(f, _NSEC_*SECTOR_SIZE, SEEK_SET);

            

             // читаем файл блоками, тщательно следя за тем, чтобы не вылететь

             // за пределы его оригинального размера

             while(p)

             {

                      // внимание! для обработки файлов с искаженным размером категорически

                      // не рекомендуется использовать функцию fgetc, поскольку в большин-

                      // стве своих реализаций она обрабатывает файл не байтам, но блоками

                      // заранее неизвестного размера. Т.е. она осуществляет прозрачную

                      // буферизацию ввода, опираясь на установленный размер файла как на

                      // эталонный. Если же установленный размер файла искажен, то нет

                      // никаких гарантий, что функция fgetc не залезет за пределы диска

                      // со всеми отсюда вытекающими последствиями (особенно это вероятно,

                      // если обрабатываемый файл - последний на лазерном диске), так что

                      // используйте fread, а еще лучше ReadFile, который уж точно не

                      // полезет в пекло поперед батьки

                      fread(buf, 1, a = _MIN(p,BLOCK_SIZE), f);
     // читаем очередной блок

                      

                      print_hex(buf,a);
p-= a                                // выводим его на экран

             }

}

Листинг 73 [crackme.27AF7A2Dh] Демонстрация обработки файлов с искаженными атрибутами стартового сектора и длины. Перед первым запуском программы, стартовый сектор защищенного файла должен быть уменьшен на величину _NSEC_, а размер увеличен по меньшей мере на 2048*_NSEC_ байт, верхнего же ограничения на максимальную длину нет (все 32-бита поля длины – ваши).

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


 Применим отладчик Soft-Ice. "… Винии поплевал на лапки для храбрости и набрал заветную команду soft-ice." (почти по Щербакову)(листинг 8.6).

Листинг 8.6. Протокол работы с Soft-Ice

:bpx CreateFileA                        ; ставим точку останова на CreateFileA

:x                                                      ; выходим из айса



Break due to BPX KERNEL32!CreateFileA (ET=3.37 seconds)

; отладчик всплывает, значит, CreateFileA кем-то только что была вызвана

; но вот кем? пытаемся определить это по имени открываемого файла

:d esp->
4                                       ; смотрим первый слева аргумент, передаваемый функции

0010:0040706C 4D 3A 5C 44 65 70 65 63-68 65 20 4D 6F 64 65 5C  M:\Depeche Mode\

0010:0040707C 30 31 20 2D 20 50 65 72-73 6F 6E 61 6C 20 4A 65  01 - Personal Je

0010:0040708C 73 75 73 2E 6D 70 33 00-4D 3A 5C 44 65 70 65 63  sus.mp3.M:\Depec

; ага! это как раз то что нам нужно!

:p ret                                                  ; выходим из функции

:? Eax                                                  ; подсматриваем значение дескриптора открытого файла

00000030  0000000048  "0"       ; дескриптор равен 0x30 (или 48 в десятичной нотации)

:bpx SetFilePointer     if (esp->
4 == 0x30);

:bpx ReadFile                   if (esp->
4 == 0x30)

; устанавливаем точки останова на основные файловые функции SetFilePointer и ReadFile,

; ReadFile, заставляя отладчик всплывать тогда и только тогда, когда им передается

; "наш" дескриптор! (специальное замечание для разработчиков защиты: господа,

; давайте же, право, себя так легко обмануть! открывайте файл несколько раз подряд и

; и попеременно работайте с ним через различные дескрипторы, - это сильно затрудняет

; затрудняет анализ)

:x                                                      ; выходим из отладчика



Break due to BPX KERNEL32!SetFilePointer  IF ((ESP->
4)==0x30) (ET=76.19 microseconds)




; это сработала точка останова на SetFilePointer, теперь нам необходимо подсмотреть

; значение offset на которое смещается указатель и origin – чтобы определить

; относительно какой части файла осуществляется отсчет

:? esp->
8                                       ; смотрим второй слева аргумент функции

00002000  0000008192  "  "      ;       указатель смещается на 0x2000 байт относительно…

:? esp->
0C                                      ; смотрим третий слева аргумент функции

00000000  0000000000  "  "      ;       …относительно начала файла (SEEK_SET)

:p ret                                                  ; выходим из отладчика



; больше функция SetFilePointer не вызывается, но зато наблюдается многократные

; вызовы функции ReadFile. Для анализа защитного кода мы не будем давать P RET

; (как это рекомендуют делать некоторые хакерские руководства). Ведь ReadFile

; скорее всего вызывается не напрямую, а из библиотечной функции-обертки,

; анализ которой нам ничего не даст. Лучше посмотрим стек вызовов…



:stack                                                  ; смотрим стек вызовов

12F8C8          401E1C                  KERNEL32!ReadFile

12F8F8          4010E5                  crackme!.text+0E1C

12FFC0          77E87903        crackme!.text+00E5

12FFF0          0                       KERNEL32!SetUnhandledExceptionFilter+005C

; адрес 55E87903h очевидно принадлежит недрам операционной системы и потому нам

; не интересен, адрес 401E1Ch (адрес возврата из ReadFile) там как же неинтересен,

; поскольку, как мы уже и говорили, скорее всего принадлежит библиотечной функции-

; обертке, а вот на адрес 4010E5h имеет смысл взглянуть:

:u 4010E5

001B:00401072           MOV    EDI, 36CD63            ; EDI := 36CD63

001B:004010C8           CMP    EDI,00000666            ; \                                ß (1)

001B:004010CE           MOV    ESI,EDI                         ;  +- ESI := _min(0x666, EDI)



001B:004010D0           JL     004010D7                ;  +

001B:004010D2           MOV    ESI,00000666            ; /

001B:004010D7           PUSH   EBX                             ; …

001B:004010D8           PUSH   ESI                             ; кол-во читаемых элементов

001B:004010D9           LEA    EAX,[ESP+14]            ; получаем указатель на буфер

001B:004010DD           PUSH   01                              ; размер одного элемента

001B:004010DF           PUSH   EAX                             ; передаем указать на буфер

001B:004010E0           CALL   00401141                ; эта функция вызывает ReadFile

001B:004010E5           LEA     ECX,[ESP+1C]            ; получаем указатель на буфер

001B:004010E9           PUSH   ESI                             ; …

001B:004010EA           PUSH   ECX                             ; …

001B:004010EB           CALL   00401000                ; обрабатываем прочитанные данные

001B:004010F0           ADD    ESP,18                          ; вычищаем ненужные аргументы из стека

001B:004010F3           SUB    EDI,ESI                         ; EDI := EDI - _min(0x666, EDI)

001B:004010F5           JNZ    004010C8        ; мотаем цикл, пока есть что обрабатывать (1) à

001B:004010F7           POP    ESI                             ; …

; изучение окрестностей адреса 4010E5 позволяет за считанные минуты восстановить

; алгоритм обработки файла. файл читается кусками по 0x666 байт до тех пор пока

; этих байт не наберется ровно 0x36CD63 (или 3.591.523 в десятичной нотации)

Листинг 74 протокол работы с soft-ice

Таким образом, после открытия файла его указатель смещается вперед на 0x2000  байт (4 сектора), а затем из файла считывается 3 .591 .523 байт данных, после чего его обработка прекращается. Следовательно, защищенный файл может быть восстановлен так…

Попробуйте его проиграть каким ни будь MP3-плейером. Если все было сделано правильно, то вы окунетесь в ритмичные звуки Depeche Mode, не омраченные более никакими защитами! Под такую музыку очень хорошо заниматься усилением защитных механизмов, а усиливать здесь есть чего!



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

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

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

Оригинальную длину файла хранить в виде константы крайне нежелательно, т. к. при анализе программы все константы сразу же бросаются в глаза и нужное значение быстро находится даже тупым перебором (большинство программ содержат не так уж много констант, соизмеримых по величине с длинами обрабатываемых файлов). Храните не длину файла, ано длину его "хвоста", т. е. остаток, полученный от деления оригинальной длины на размер обрабатываемых блоков.


Естественно, размер блоков и их количество блоков так же придется где-то хранить, но… проанализировать взаимосвязь трех констант значительно труднее одной!

Учитывая все вышесказанноеранее сказанное, мы сможем значительно усилить нашу защиту. Один из вариантов ее реализации может выглядеть, например, так как показано в листинге 8.7.:

Листинг 8.7. [crackme.CEE99D84h.c] Программная реализация защитного механизма, основанного на искаженном оглавлении диска

// размер "хвоста" последнего блока

#define TAIL_SIZE                     (_FSIZ_ % BLOCK_SIZE)

// кол-во целых блоков

#define N_BLOCKS                      (_FSIZ_ / BLOCK_SIZE /2)

// ХОЛОСТАЯ ОБРАБОТКА ДАННЫХ

// -------------------------

// желательно сделать эту функцию как можно более сложной и запутанной,

// чтобы факт ее холостой работы был не так очевиден

threshing(unsigned char *src, int n)

{

              int a, sum=0;for (a = 0; a< n; a++) sum += src[a]; return sum;

}

main()

{

              int  a = 0;

              long p = _FSIZ_;

              FILE *f_even, *f_uneven, *f_threshing;

              char buf[BLOCK_SIZE + (_NSEC_*SECTOR_SIZE)];

             

              // TITLE

              fprintf(stderr, "crackme 27af7a2d by Kris Kaspersky\n");

             

              // пытаемся открыть файл

              // f_threshing лучше всего открывать первым, т.к. первый же встретившийся

              // хакеру вызов CreateFileA должен давать "подсадной" дескриптор

              if (            ((f_threshing        = fopen(_FN_, "rb")) == 0) ||

                          ((f_even              = fopen(_FN_, "rb")) == 0) ||

                          ((f_uneven          = fopen(_FN_, "rb")) == 0))

                          { fprintf(stderr, "-ERR: can not open %s\n",_FN_);
return -1;}

             

              // устанавливаем f_even

              fread(buf, 1, _NSEC_*SECTOR_SIZE, f_even);



             

              // имитируем пропуск NSEC*SECOR_SIZE/2 байт (вдруг хакер на это купится?)

              // на самом же деле, все NSEC*SECTOR_SIZE первый байт файла идут в муор-

              // ное ведро

              threshing(buf + _NSEC_*SECTOR_SIZE/2,_NSEC_*SECTOR_SIZE/2);

             

              // устанавливаем f_uneven

              fread(buf, 1, _NSEC_*SECTOR_SIZE+BLOCK_SIZE, f_uneven);

             

              // имитируем пропуск NSEC*SECOR_SIZE/3 байт

              threshing(buf + _NSEC_*SECTOR_SIZE/3,  2*_NSEC_*SECTOR_SIZE/3+BLOCK_SIZE);


             

              // устанавливаем threshing, пуская хакера по ложному следу

              fseek(f_threshing,_NSEC_*SECTOR_SIZE/4,SEEK_SET);

             

              // читаем файл блоками, тщательно следя за тем, чтобы не вылететь

              // за пределы его оригинального размера

             

              for (a=0; a < N_BLOCKS; a++)

              {

                      // читаем данные в холостую

                      fread(buf, 1, BLOCK_SIZE, f_threshing);
        threshing(buf,BLOCK_SIZE);

                      

                      // читаем четный действительный блок

                      fread(buf, 1, BLOCK_SIZE, f_even);
                    print_hex(buf,BLOCK_SIZE);

                      

                      // пропуск нечтного блока для дескрпитора f_even

                      fread(buf, 1, BLOCK_SIZE, f_even);
                    threshing(buf,BLOCK_SIZE);

                      

                      // читаем нечетный действительный блок

                      fread(buf, 1, BLOCK_SIZE, f_uneven);
                print_hex(buf,BLOCK_SIZE);

                      

                      // пропуск уже четного блока для дескрпитора f_uneneven

                      fread(buf, 1, BLOCK_SIZE, f_uneven);
                threshing(buf,BLOCK_SIZE);

              }

              // дочитываем хвост

              fread(buf, 1, TAIL_SIZE, f_even);
print_hex(buf, TAIL_SIZE);



}

Листинг 75 [crackme.CEE99D84h.c]  программная реализация защитного механизма, основанного на искаженном оглавлении диска

Попробуйте взломать эту защиту. Что, не получается? Попытка отследить вызовы файловых функций SetFilePoiner и ReadFile ничего не дает, т. к. данные считываются глубоко нелинейным образом и способов быстрого отделения "зерен" от "плевел" здесь не существует. Такие защиты вообще не "ломаются" в отладчике, —– тут требуется помощь дизассемблера, но и с дизассемблером на скорый успех рассчитывать не приходится. Сложность и запутанность алгоритма обработки данных значительно усложняет анализ программы и на определение действительных границ файла даже у профессионала может уйти несколько часов (а в некоторых случаях и дней!). По соображениям экономии места дизассемблерные листинги и описание процесса взлома здесь не приводятся, поскольку в них нет ничего интересного —– просто тупая рутина и все. Единственная зацепка —– функция Threshing, имитирующая обработку данных. Как только хакер поймет, что результаты ее работы никак не используются в программе, он тут же продвинется далеко вперед. Контрольные точки останова на чтение/записи памяти позволяют быстро и элегантно определить: происходит ли обращение к заданным ячейкам или нет. Короче говоря, нет ничего тайного, что при помощи отладчика Soft-Ice и дизассемблера IDA Pro не стало бы явным…


Содержание раздела