Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Сегодня я покажу тебе несколько приемов обращения с Radare2 — мощным фреймворком для обратной разработки. Мы выведаем скрытый в программе пароль, для чего научимся извлекать из исполняемых файлов структурированную информацию и конвертировать получаемые значения. Затем изменим исполняемый файл и выполним недоступную при корректном поведении функцию.
Упражняться мы будем на crackme, которую я написал специально для демонстрации. Скачать файл ты можешь с моего GitHub. Все действия мы будем проводить в Debian Linux.
В статье «Radare2 с самого начала. Учимся использовать опенсорсный фреймворк для анализа приложений в Linux» мы уже начали исследовать этот исполняемый файл. При запуске он открывает сокет и начинает слушать порт 14884. Если подключиться к этому сокету при помощи netcat, то можно увидеть приглашение для ввода имени пользователя или пароля. Если ввести неверный пароль, сеанс завершится.
Используя основные возможности Radare2, мы выявили в программе функции main
, authenticate
, check_username
, check_password
, start_server
и несколько других. В authenticate
есть пять локальных переменных, а также вызов функции с говорящим названием check_username
, которой в качестве единственного аргумента передается значение переменной fd
.
Это краеугольная функция в защитном механизме, поскольку из нее вызываются некоторые другие весьма важные и не всегда относящиеся к процедуре логина функции. На очереди — извлечение пароля. Но даже когда мы раскусим защитный механизм, у нас будет возможность копать этот крякмис в глубину. Мы изменим его код, чтобы выполнение программы шло по той ветви, результаты выполнения которой нам нужны.
Дизассемблированная функция check_username
В функции check_username
дескриптор сокета, переданный в аргументе, помещается в переменную fildes
. Далее с помощью библиотечной функции memset
готовится буфер памяти: src
заполняется нулями, затем в него с помощью функции read
читается пользовательский ввод из сокета, на который указывает fildes
. То есть читается как бы с удаленного устройства.
Дальше в консоль на стороне сервера функция printf
выводит строку [+] Reading username
, потом в нее же с помощью fputs
выливается содержимое буфера src
, содержащего введенное имя пользователя. Далее возвращенный функцией fputs
результат сравнивается с -1
. Если равенство верно, выводится сообщение об ошибке, если же возвращенное значение не равно -1
(ноль или положительное значение), то выполняется переход на строку 0x1605
. Здесь происходит вывод символа конца строки — n
.
После этого готовятся параметры для вызова библиотечной функции strcpy
. Она копирует имя пользователя src
в новую область памяти — dest
. Далее в строке со смещением 0x1628
в переменную var_420h
копируется значение 0x6262616a
. В комментарии рядом Radare2 оставил метку, заключенную в одинарные кавычки, — jabb
:
mov dword [var_420h], 0x6262616a ; 'jabb'
По мнению Radare2, это шестнадцатеричное число — набор букв в кодировке UTF-8, используемой в большинстве дистрибутивов Linux. Доверяй, но проверяй! В командную строку под дизассемблированным листингом функции введи
? 0x6262616a
Ниже появится список, в котором указанное значение будет конвертировано в разные типы данных.
Приведение числа 0x6262616a к разным типам данных
Нас интересует тип string
. Напротив него мы видим строку jabb
, что и требовалось доказать.
Неверно введенное имя пользователя
Вернемся в функцию check_username
. Строкой ниже (0x1632
) мы видим, что в переменную var_21ch
помещается символ a
, пока непонятно для чего. Снова промотаем листинг к началу функции, где расположены комментарии о переменных:
... ; var int64_t var_41ch @ rbp-0x41c ; var int64_t var_420h @ rbp-0x420 ...
Нас интересуют эти две переменные. Теперь посмотрим на код присвоения им значений:
mov dword [var_420h], 0x6262616a ; 'jabb'mov word [var_41ch], 0x61 ; 'a'
Истина где‑то рядом. В UTF-8 символ может занимать от одного до четырех байтов, однако латинские символы, которые мы видим в листинге, никогда не превышают одного байта. Таким образом, значение 0x6262616a
— это четыре байта, что подтверждает размер приемника — двойное слово, dword.
Источник: xakep.ru