Демонстрационный пример взлома
Давайте, в качестве упражнения напишем простейшую триальную защиту и попробуем ее взломать методом сравнения покрытий до истечения испытательного срока и после. Исходной код подопытной программы может выглядеть, например, так, как показано в листинге 2. Срок "годности" задается константами ye (год), me (месяц), md (день), которые должны быть установлены в соответствии с текущей датой (так, чтобы программа еще работала).
int expired = 0;
#define md 3
#define me 8
#define ye 2006
foo()
{
SYSTEMTIME sy;
GetSystemTime(&sy);
if ((sy.wYear&0xF)*sy.wMonth*sy.wDay > (ye&0xF)*me*md) expired=1;
}
main()
{
printf("coverage trial\n");
foo();
if (expired)
{
printf("trial has expired!\n");
}
else
{
printf("trial ok\n");
}
}
Листинг 2 листинг программы coverage.c, защищенной испытательным сроком, которую мы будем ломать
Компиляция осуществляется так же, как и в предыдущем случае, но лучше использовать готовый файл с диска — это гарантирует совпадение всех смещений и если вдруг в ходе взлома возникнут затруднения, всегда будет ясно где лыжи и откуда копать.
Запускаем coverage.exe, убеждаемся, что работает (программа пишет: trial ok), а затем переводим дату за конец испытательного срока и запускаем вновь. Ага! trial has expired! Значит, надо ломать!
Рисунок 6 демонстрационная программа coverage.exe, с ограниченным сроком использования
Возвращаем дату на прежнее место и загружаем coverage.exe в OllyDbg. Затем в "View" выбираем "Run trace" и нажимаем <SHFT-F10> для вызова контекстного меню в котором говорим "log to file" и в появившемся диалоговом окне вводим имя файла — "coverage1.txt". После чего нажимаем <CTRL-F11> (Trace Into) и дожидаемся завершения работы программы.
На диске образуется файл coverage1.txt размером ~3 Мбайта. В том же самом окне "Run trace" нажимаем <SHIFT-F10> еще раз и закрываем лог-файл ("close log file").
Перезапускаем программу по <CTRL-F2>, переводим системную дату вперед (из командной строки командной date или щелкнув по часам в правом нижнем углу), возвращаемся в окно "Run Trace", нажимаем <SHIFT-F10>, "log to file" и вводим имя "coverage2.txt". Запускаем трассировку по <CTRL-F11> и по завершении выполнения программы закрываем log-файл (<SHIFT-F10>, "close log file").
Рисунок 7 определение покрытия (Run Trace) с помощью отладчика OllyDbg
Теперь у нас есть два log-файла, которые можно скормить утилите log-coverage-diff.exe, чтобы увидеть различия в покрытии до истечения испытательного срока и после:
$log-coverage-diff.exe coverage1.txt coverage2.txt
diff p1 -> p2
00401076 Main PUSH coverage.00406054
0040107B Main CALL coverage.00401085
00401080 Main ADD ESP,4
diff p2 -> p1
0040103B Main MOV DWORD PTR DS:[4068F0],1
00401067 Main PUSH coverage.00406040
0040106C Main CALL coverage.00401085
00401071 Main ADD ESP,4
00401074 Main JMP SHORT coverage.00401083
Листинг 3 результат сравнения покрытия программы до и после истечения испытательного срока
Проанализировав полученный результат (см. листинг 3), даже начинающий хакер может долгаться, что двойное слово по адресу 4068F0h представляет собой глобальный флаг истечения испытательного срока, а команда "MOV DWORD PTR DS:[4068F0], 1" — как раз и есть та редиска, которая его взводит, когда испытательный срок заканчивается. Чтобы заполучить программу в бессрочное использование достаточно заменить "MOV DWORD PTR DS:[4068F0], 1" на "MOV DWORD PTR DS:[4068F0], 0".
Загружаем coverage.exe в hiew, дважды нажимаем <ENTER> для перехода в дизассемблерный режим, давим <F5> (Goto) и вводим адрес команды MOV, предварив его символом точки, чтобы указать hiew'у, что это именно адрес, а не смещение от начала файла — ".40103B", затем нажимаем <F3> для активации режима редактирования и <ENTER> для ввода ассемблерной команды.
hiew автоматически копирует текущую инструкцию в строку редактирования и нам остается всего лишь заменить "1" на "0", нажать <F9> для сохранения изменений в файле и после выхода из hiew'а его можно запускать невзирая на текущую дату.
Рисунок 8 взлом программы coverage.exe в hiew'e
Как вариант, можно открыть coverage.exe в IDA Pro и посмотреть, что находится по адресам, выданным утилитой log-coverage-diff.exe и вокруг них. Возможно, мы найдем более быстрый путь взлома:
0401000 sub_401000 proc near
0401000 push ebp
0401001 mov ebp, esp
0401003 sub esp, 10h
0401006 lea eax, [ebp+SystemTime]
0401009 push eax ; lpSystemTime
040100A call ds:GetSystemTime
0401010 mov ecx, dword ptr [ebp+SystemTime.wYear]
0401013 and ecx, 0FFFFh
0401019 and ecx, 0Fh
040101C mov edx, dword ptr [ebp+SystemTime.wMonth]
040101F and edx, 0FFFFh
0401025 imul ecx, edx
0401028 mov eax, dword ptr [ebp+SystemTime.wDay]
040102B and eax, 0FFFFh
0401030 imul ecx, eax
0401033 cmp ecx, 90h
0401039 jle short loc_401045
040103B mov dword_4068F0, 1
0401045 loc_401045:
0401045 mov esp, ebp
0401047 pop ebp
0401048 retn
0401048 sub_401000 endp
0401048
0401049 _main proc near
0401049 push ebp
040104A mov ebp, esp
040104C push offset aCoverageTrial ; "coverage trial\n"
0401051 call _printf
0401056 add esp, 4
0401059 call sub_401000
040105E cmp dword_4068F0, 0
0401065 jz short loc_401076
0401067 push offset aTrialHasExpire ; "trial has expired!\n"
040106C call _printf
0401071 add esp, 4
0401074 jmp short loc_401083
0401076 loc_401076:
0401076 push offset aTrialOk ; "trial ok\n"
040107B call _printf
0401080 add esp, 4
0401083
0401083 loc_401083:
0401083 pop ebp
0401084 retn
Листинг 4 фрагмент дизассемблерного листинга ломаемой программы coverage.exe, различия в покрытии до и после истечения испытательного срока выделены подчеркиванием
Как можно видеть, сердце защитного механизма сосредоточено в процедуре sub_401000, которая сравнивает текущую дату с жестко прошитой (hard-coded) константой, после чего делает jle на loc_401045 (испытательный срок еще не истек) или… не делает и тогда выполняется команда mov dword_4068F0, 1, устанавливающая глобальный флаг истечения испытательного срока, проверяемый лишь в одном-единственном месте — по адресу 040105Eh, за которым идет условный переход 0401065 jz short loc_401076, выбирающий какое из двух сообщений выводить на экран (в реальных программах флаг регистрации обычно проверяется многократно). Если мы заменим jle short loc_401045 на jmp short loc_401045, флаг истечения испытательного срока никогда не будет взведен!
Рисунок 9 наглядное отображение расхождения в покрытии исследуемой программы в IDA Pro, выполненное специальным скриптом и оформленное в виде комментариев
Для наглядности можно написать несложный скрипт на IDA-Си, считывающий результат работы log-coverage-diff.exe и отмечающий различия в покрытиях каким ни будь символом, например, "*1*" будет означать, что данная машинная команда выполнялась только в первом прогоне, а "*2*" – только во втором (см. рис. 9).Подробности о синтаксисе IDA-Си и технике написания скпитов — в книге "образ мышления — IDA PRO" электронную копию которой можно бесплатно скачать с мыщъх'иного сервера ftp://nezumi.org.ru