Фундаментальные основы хакерства. Новые способы находить защитные механизмы в чужих программах

Чтобы иметь возможность ломать программы, на страже которых стоят хитрые защитные механизмы, необходимо знать разные способы нахождения этих самых механизмов в подопытном приложении. В сегодняшней статье будет показано, как это сделать, а в конце мы разберем пример работы с графическим приложением.

INFO

Пятнадцать лет назад эпический труд Криса Касперски «Фундаментальные основы хакерства» был настольной книгой каждого начинающего исследователя в области компьютерной безопасности. Публикуемый «Хакером» одноименный цикл статей — попытка обновить эту книгу с учетом современных реалий. Начать чтение ты можешь со статьи «Проверка аутентичности и базовый взлом защиты».

В прошлой статье мы узнали, каким образом соотнести адреса байтов в виртуальной памяти с их реальным расположением на носителе. Это потребовало от нас напрячь мозг и применить математику. Между тем, как мы увидели из предыдущих статей, непосредственный взлом, когда известно месторасположение защитного механизма, представляет собой элементарную задачу, которую легко решить с помощью HIEW или другого редактора PE-файлов.

Способ 1. Прямой поиск введенного пароля в памяти

Пароль, хранящийся в теле программы открытым текстом, — скорее из ряда вон выходящее исключение, чем правило. К чему услуги хакера, если пароль и без того виден невооруженным взглядом? Поэтому разработчики защиты всячески пытаются скрыть его от посторонних глаз (о том, как именно они это делают, мы поговорим позже).

Впрочем, учитывая размер современных пакетов, программист может без особого труда поместить пароль в каком-нибудь завалявшемся файле, попутно снабдив его «крякушами» — строками, выглядящими как пароль, но паролем не являющимися. Попробуй разберись, где тут липа, а где нет, тем более что подходящих на эту роль строк в проекте средней величины может быть несколько сотен, а то и тысяч!

Давай подойдем к решению проблемы от обратного — будем искать не оригинальный пароль, который нам неизвестен, а ту строку, которую мы скормили программе в качестве пароля. А найдя, установим на нее бряк, и дальше все точно так же, как и раньше. Бряк всплывает на обращение по сравнению, мы выходим из сравнивающей процедуры, корректируем JMP и...

Взглянем еще раз на исходный текст ломаемого нами примера passCompare1.cpp:

for(;;)
{
    printf("Enter password:");
    fgets(&buff[0],PASSWORD_SIZE,stdin);
    if (strcmp(&buff[0],PASSWORD))
        printf("Wrong password\n");
    else break;
    if (++count>2) return -1;
}

Обрати внимание — в buff читается введенный пользователем пароль, сравнивается с оригиналом, затем (при неудачном сравнении) запрашивается еще раз, но (!) при этом buff не очищается! Отсюда следует, что, если после выдачи ругательства Wrong password вызвать отладчик и пройтись по памяти контекстным поиском, можно обнаружить тот заветный buff, а остальное уже дело техники!

Итак, приступим (мы еще не знаем, во что мы ввязываемся, — но, увы, в жизни все сложнее, чем в теории). На этот раз запустим passCompare1.exe отдельно от отладчика. Затем подключимся к процессу из отладчика (Attach to process в WinDbg). Обрати внимание: в окне выбора процесса отображаются все запущенные процессы и для каждого из них выводится его разрядность в столбце Platform. Вводим любой пришедший на ум пароль (например, KPNC Kaspersky++), пропускаем возмущенный вопль Wrong мимо ушей и в отладчике нажимаем Break (сочетание клавиш Alt + Del).

Окно со списком процессов для выбора

Попробуем отыскать в памяти введенный пароль:

0:001> s -a 0x0 L?0x7FFFFFFF "KPNC Kaspersky"

Пояснения

Первый параметр после команды s — флаг -a — определяет цель поиска как набор ASCII-символов. Второй параметр — смещение, откуда начать искать. Вообще-то начинать поиск с нулевого смещения — идея глупая. Судя по карте памяти, здесь расположен служебный код и искомого пароля быть не может. Впрочем, это ничему не вредит, и так гораздо быстрее, чем разбираться, с какого адреса загружена программа и откуда именно начинать поиск.

Третий параметр — верхний предел поиска, то есть «докуда». Здесь у нас стоит максимальное 32-битное знаковое число, таким образом мы охватываем весь возможный диапазон 32-битного процесса.

Последний параметр — собственно искомая строка. Обрати внимание, что мы ищем не всю строку, а только ее часть (KPNC Kaspersky++ против KPNC Kaspersky). Это позволяет избавиться от ложных срабатываний, возникающих из-за ссылок на внутренние буфера.

Результат (у тебя значения, скорее всего, получатся другими, и они будут меняться при каждом перезапуске приложения):

00f9f810 4b 50 4e 43 20 4b 61 73-70 65 72 73 6b 79 2b 2b KPNC Kaspersky++
0147fd80 4b 50 4e 43 20 4b 61 73-70 65 72 73 6b 79 2b 2b KPNC Kaspersky++

Целых два вхождения! Почему два? Предположим, что при чтении ввода с клавиатуры символы сперва попадают в системный буфер, который и дает ложное срабатывание. Тем не менее не ставить же, не разобравшись, сразу обе точки останова. В данном случае четырех отладочных регистров процессора хватит, а как быть, если бы мы нашли десяток вхождений? Да и в двух бряках немудрено заблудиться с непривычки! Как отфильтровать помехи?

Начинаем думать.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


"Юрий Язев : Широко известен под псевдонимом yurembo. Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер».."

Комментарии (6)