Пролог
Всем добрый день. Позвольте предложить вам небольшую экскурсию в интересный, но всё более утрачивающий своё значение сегмент мира информационной безопасности — защиты приложений. Можно расценивать это как логическую игру с разными, заранее не оговоренными вариантами решения и поиском нестандартных подходов, но объединять все эти решения должна одна цель — получение доступа к приложению посредством снятия защиты.
Для кого? Искушенные в этом деле люди с каким-либо нормальным опытом не найдут здесь чего-либо интересного, скорее эта статья для «общего развития», для ознакомления с данной тематикой.
Постановка задачи и первое приближение
Речь пойдет о старой, но очень добротно сделанной компьютерной игре. Конечно, 2000-ный год выпуска — это звучит смешно, но в то же время этот факт позволяет абстрагироваться от законодательства и на 99% быть уверенным, что сам правовой факт взлома данного ПО никого не заинтересует.
Итак, достали «с полки», сдули пыль, установили, запускаем — выпадает уведомление о просьбе вставить диск.
Проблема есть, теперь перейдем к решению.
Акт I — Лобовая атака
Запускаем процесс дизассемблирования ехе-файла. Готово, видим ассемблерный листинг, различные сегменты кода и данных. Больше 1.7мб информации, с чего же начать? Начать надо с размышления на тему того, что никто не будет усложнять себе жизнь и писать всё с нуля, а наверняка воспользуется уже готовыми функциями. Что можно использовать при программировании на Винде в начале столетия? Конечно, WinApi!
Если есть какое-то общение с диском — следовательно, копать надо в плане обращений к диску. Гуглим какой-нибудь справочник, есть. Цель — общение с диском, диск — Disk, Drive, Volume — отлично, находим GetDriveType — вроде то что надо, будем надеяться, что сия функция используется в нашей программе. Запускаем поиск по листингу — ура, то что надо:
Первый раз появление метода GetDriveType мало о чем говорит, а вот второй раз — сразу после него идет проверка на равенство 5, что же это означает?
0 — Неизвестное устройство
1 — Диск не найден
2 DRIVE_REMOVABLE — Гибкий
3 DRIVE_FIXED — Жесткий
4 DRIVE_REMOTE — Сетевой диск
5 DRIVE_CDROM — CD-ROM
6 DRIVE_RAMDISK — RAM диск
Решение напрашивается — открываем HEX-редактор и меняем второй операнд в функции сравнения — получаем:
cmp eax, 3; проверка — если жесткий диск
Хм, стало быть дело в шляпе? Запускаем, проверяем — не тут-то было.
Что же, первый натиск не получился, но у нас есть ещё функции, с входнымивыходными значениями можно «поиграть».
Здесь я опущу подоробное описание, скажу лишь, что и дальнейшая перетасовка параметров ничего не дала — программа обходила все существующие тома на машине, логировала их значения в удаленном сегменте данных в младших тетрадах ячеек памяти. Причем всё это было «разбавлено» постоянными прыжками по коду и многочисленными вызовами функций — как WinApi из user32.dll и kernel32.dll, так и внутренних. Полученные значения внутри функций заносились по одному в стек и в конце концов, когда я увидел несколько раз повторяющийся вызов со «стопкой» pop-операций — понял, что лобовая атака захлебывается окончательно, уж слишком сильную для моих сил оборонительную систему выстроили разработчики.
Конечно, этой проблемой и занимается реверсивная инженерия, но прикладывать такие силы на разбор и детальный анализ ну никак не хотелось — быть может, воспользуемся поговоркой — «Умный в гору не пойдет, умный гору обойдет?»
Акт II — Черный вход
Действительно, что если пойти с обратного конца? У нас есть конкретные фразы, которые мы получаем — 2 строчки в MessageBox. Ок, поехали: запускаем поиск на предмет «please insert starship troopers» и сразу же попадаем в сегмент данных с меткой «Text». Просто так, обычный «Text». Отлично, находим использование данной метки в сегменте текста, оно одно — и где же? Как раз в методе вызова MessageBox:
sub_4013C0 proc near; CODE XREF: WinMain(x,x,x,x)+15Ap
push 0 ; uType
push offset Caption; «Failed to find Starship Troopers CD»
push offset Text; «Please insert Starship Troopers CD and »…
push 0; hWnd
call ds:MessageBoxA
retn
sub_4013C0 endp
Хорошо, а где же ссылаемся на данную метку?
Теперь всё становится на свои места: попадя в данный блок, мы и вызываем NAG-MessageBox и провоцируем некий недоброжелательный ret.
«Да куда не пойдешь, везде наткнешься на ret...»
Нет, нам он не нужен — следовательно в этот блок нам попадать не надо. Как мы туда попадаем? Нехитрой командой «короткого» перехода jnz — «jump if not ZF». Ещё не догадались, что нужно сделать? Нужно поменять проверку — вместо jnz поставить jz — «jump if ZF».
Идем в HEX-редактор, по указанному рядом адресу и меняем код команды с 0x75 на 0x74 — наш заветный прыжок при нулевом флаге.
Дело за малым — запустить и проверить, что наша защита успешно пробита!
Ставим брейкпоинт, запускаем дебаг и наблюдаем наш jump в нужный блок кода, где уже и начинается загрузка и инициализация игровых данных.
Эпилог
Конечно, данная защита — детский лепет с тем, что можно встретить в современных приложениях. Но сам факт элегантности найденного способа — ведь по факту мы меняем код одной команды — с «5» на «4», т.е. с «110» на «100». Поменяв всего один БИТ(!) в ехе-файле размером в 1.7мб — мы полностью снимаем защиту в виде привязки к диску. Да и сам процесс разборки кода неплохо стимулирует мозговую деятельность.
Буду надеяться, что, прочитав эту заметку, человек сможет получить небольшое поверхностное представление о том, какими методами могут быть сделаны nocd, nodvd и прочие прелести, доступные в просторах интернета.
Были полезными ответы гугла в виде:
exelab.ru
www.whatis.ru
Автор: Vetalek