Здорово, когда программы разрешают себя отлаживать: какие бы тайны ни скрывали, выдадут. Честным программам скрывать нечего, но встречаются и вредные: такие программы мешают себя изучать, а то и вовсе отказываются работать.
Отладчик поможет изучить зашифрованный код. Программа расшифровывает код перед выполнением: проще остановить программу отладчиком и исследовать, чем расшифровывать код самостоятельно. Программа сопротивляется отладке, когда хочет этому помешать.
Посмотрим, как справиться с противодействием отладке на примере 1337ReverseEngineer's The Junkrat https://crackmes.one/crackme/62dc0ecd33c5d44a934e9922 .
![](https://habrastorage.org/getpro/habr/upload_files/3e3/c17/146/3e3c17146fc4b233a558871e30fdb859.png)
Junkrat ожидает ввода с клавиатуры и отвечает "Correct key!" на верный пароль, иначе - "Invalid password". Задача - узнать верный пароль.
![](https://habrastorage.org/getpro/habr/upload_files/b0e/faa/39b/b0efaa39b11c9fe897a1e0fd5df03283.png)
Код проверки пароля решает, какое сообщение показать: найдем сообщения - найдем и код. Поиск строк завершился неудачей: строки зашифрованы.
Программа отказывается работать под отладчиком: сразу завершается. Cхитрим: пока программа ожидает ввод с клавиатуры, запустим отладчик и присоединимся к процессу, а после выполним команду отладчика "Run to user code", чтобы остановиться, как только программа получит ввод.
Дизассемблируем программу и найдем вызов fgets. Автор программы нарочно запутал код: добавил лишних инструкций, из-за которых дизассемблер распознал бесполезные локальные переменные. Среди них придется отыскать те, что связаны с кодом проверки пароля.
![](https://habrastorage.org/getpro/habr/upload_files/8f1/cd0/854/8f1cd0854fbb536d22392bbe508ac893.png)
![](https://habrastorage.org/getpro/habr/upload_files/0b6/fdf/95c/0b6fdf95c397b81d5589cf1bb8094437.png)
Догадались, что программа работает в цикле: после ввода неверного пароля программа снова запрашивает пароль. Предположим, что программа завершается, когда получит верный пароль.
![](https://habrastorage.org/getpro/habr/upload_files/255/d39/5a2/255d395a266962aa252570f0f4b7c863.png)
![](https://habrastorage.org/getpro/habr/upload_files/34b/6ef/6ff/34b6ef6ff90d28b0020d1c3c91b3b2d8.png)
Код вызывает функцию system("PAUSE") и выходит из цикла, а перед ним - проверка условия cmp. Выше - цикл: по инструкциям
mov ecx, [ebp+var_1C8]
mov dl, [ecx]
mov eax, [ebp+var_1CC]
cmp dl, [eax]
var_1C8 и var_1CC - указатели, а цикл проверяет, равны ли строки. После fgets код копирует указатель на password в переменную var_1C8, а в var_1CC - секретный пароль.
Снова запускаем программу, стыкуем отладчик и ставим breakpoint на начало цикла проверки пароля, вводим пароль, возвращаемся в отладчик и получаем секретный пароль по адресу [ebp-1CC].
![](https://habrastorage.org/getpro/habr/upload_files/745/29d/349/74529d349b2a0e3dd512d403ba238f74.png)
![](https://habrastorage.org/getpro/habr/upload_files/2ad/c44/bc4/2adc44bc440e6b18e320c1b08c275d6d.png)
Послесловие
Код полон вызовов IsDebuggerPresent и GetTickCount, чтобы замерять время между инструкциями, однако между чтением пароля и проверкой программа оказалась не защищена. Задача окажется сложнее, если зашифровать и код.
Отладчики умеют скрываться при помощи плагинов, например, ScyllaHide https://github.com/x64dbg/ScyllaHide/releases/ .
![](https://habrastorage.org/getpro/habr/upload_files/250/de0/a22/250de0a227e68ff373d7620b57ad08ba.png)