первое вхождение искомой последовательности в образе диска
Листинг 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 не стало бы явным…