QEMU и реверс образа флоппика / Блог компании Нео. БИТ / Хабрахабр. В преддверии ! Neo. QUEST — это шанс узнать что- то новое, усовершенствовать свои «хакерские» навыки, пообщаться с коллегами, понаблюдать за решающим соревнованием лучших хакеров, и просто отлично провести время! Подробнее про место проведения и темы докладов Neo. QUEST- 2. 01. 5 можно прочитать тут и там. Задание online- этапа, оставленное «на десерт», было достаточно олдскульным: достаточно уже того, что речь шла о дампе всеми давно забытого флоппи- диска! О том, как участникам квеста пришлось повозиться с реверсом и с QEMU — под катом! Что делать с исходными данными к заданию? Судя по легенде, это должен быть образ загрузочной дискеты. Попробуем скормить его утилите file. Предположение оказалось верным, это дискета. Что же, попробуем с нее загрузиться. В качестве виртуальной машины используем QEMU. Выполним qemu –fda task. По каким- то причинам загрузиться не получилось – QEMU написала “Loading” и зависла. Для начала попробуем посмотреть, что же происходит внутри виртуальной машины, подключив к ней gdb в качестве отладчика. Для подключения gdb важно знать режим работы виртуальной машины, так как это влияет на формат данных, передаваемых от приложения к отладчику. Виртуальная машина: взгляд изнутри. Перейдем в окно QEMU и нажмем Ctrl + Alt + 2, чтобы открыть консоль команд. Выполним в ней “info registers” и проскроллим вверх комбинацией Ctrl + Up. Загрузочные дискеты обычно используются в последнюю очередь, если больше никак нельзя загрузить программу установки на машине, которая не .На рисунке выше обведены поля, на которые стоит обратить внимание – CR0 и атрибуты дескриптора, на который указывает CS. Из значения CR0 и CS. ATTR следует, что включен защищенный режим без виртуальной памяти и выполняется 3. Для нас это означает, что в gdb нужно переключать режим командой set architecture i. В случае, если gdb 3. Запустим QEMU с опцией “–s”(эта опция позволяет подключать отладчик) и подключим gdb, выполнив в нем команду “target remote localhost: 1. Floppy Image - Небольшая утилита, позволяющая создавать образы дискет и, Snappy Driver Installer - программа для поиска и установки драйверов. Casio PVS · -> Свалка образов дискет · -> Архив Unix систем · -> DOS LIVE CD! Утилиты копирования информации и создания креков FDA копирует дискеты, не копируемые программами CopyWrite, Copy II PC, Disk. И тогда было принято решение создать не цифровые. Если же владелец дискеты пытался защитить ее от записи, то ПО. FDA поможет (если это та программа, про которую я думаю). Создание образов программ Чибис (2 дискеты), Клинок-2 (2 дискеты). TXT для более глубокого понимания работы программы FDA. Можно установить программу под Окнами, создать нужные каталоги для образов, Перед началом работы с FDA обязательно произведите калибровку всех Under Windows the program works, but does not make a normal image. FloppyImage - мне правда не хватило терпения сделать образ под ней. Floppy Image - Небольшая утилита, позволяющая создавать образы дискет и. Snappy Driver Installer - программа для поиска и установки драйверов. Выведем несколько инструкций вокруг EIP, и увидим, что виртуальная машина находится в HALT, при этом в стеке нули. Совершенно непонятно, откуда мы сюда попали? Похоже, придется дизассемблировать. Дизассемблирование и отладка кода. Попробуем разобраться, где происходит прыжок в HALT, последовательно дизассемблируя и отлаживая код! Можно установить программу под Окнами, создать нужные каталоги для образов. Перед началом работы с FDA обязательно произведите калибровку всех. FloppyImage - мне правда не хватило терпения сделать образ под ней. Создание образов программ Чибис (2 дискеты), Клинок-2 (2 дискеты), TXT для более глубокого понимания работы программы FDA. Начнем с первого сектора дискеты. При загрузке с флоппи в legacy режиме (а по другому, наверное, и не выйдет), BIOS читает первый сектор и грузит его по адресу 0x. Чаще всего, задача кода из первого сектора — загрузить «продолжение» с диска и перейти в защищенный режим. Посмотрим, что же за код там находится, посредством утилит dd и objdump. Немного пролистав код от начала, можно заметить переход в защищенный режим. Инструкция ljmp здесь используется для изменения селектора кода, в качестве адреса перехода стоит 0x. Так как при дизассемблировании я не указывал базу, равную 0x. Обычно это делается для того, чтобы начать выполнять 3. Дополнительно в этом можно убедиться, найдя структуру gdt, адрес которой находится в регистре gdtr, значение которого лежит по адресу 0x. В gdt нужно посмотреть на тип дескриптора со смещением в 8, это первый аргумент инструкции ljmp. Это значит, что код по адресу 0x. Выделим из task. bin по смещению 0x. В получившемся коде в селекторы загружаются новые значения, и происходит прыжок на адрес 0x. Запустим виртуальную машину и поставим брейкпоинт на этот адрес. Для этого QEMU запускается с командой qemu –s –S –fda task. Установить брейкпоинт на адрес – “b *0x. После срабатывания брейкпоинта выведем несколько инструкций. Выполним первый jmp командой “si” и снова выведем код к выполнению. Код до первого ret небогат на ветвления, и есть только один call, в котором что- то может происходить. Сдампим 4. Кб памяти по адресу 0x. Дамп памяти можно получить из gdb следующей командой: Дизассемблируем получившийся дамп командой objdump –D –b binary –m i. Нас интересует, где мы попадаем в halt, и так как в видимом коде halt’а нет — поставим брейкпоинты на все call’ы и ret в конце функции. Вот список интересующих нас адресов: 0x. A5. B, 0x. 82. A7. D, 0x. 82. A9. 1, 0x. AB0, 0x. 82. AEF, 0x. B1. 0, 0x. 82. B3. B4. 6, 0x. 82. B6. C5. 3, 0x. 82. CAD. Далее будем продолжать выполнение, последовательно вываливаясь на каждом поставленном брейкпоинте. Скачать Игру Kingdoms Lords Для Андроида there. Нас интересует брейкпоинт, после которого произойдет зависание. Им оказывается брейкпоинт, установленный на ret – в конце исследуемой функции. Это неожиданно, но если обратить внимание на push перед ret, то становится ясно, что это не возврат в точку вызова, а передача управления на новый код. Выполним si и попадаем на адрес 0x. Ура, наконец- то мы запустили задание! Что бы снова не искать call’ы и не ставить брейкпоинты руками, сделаем следующим образом – напишем простой скрипт, который будет в цикле исполнять одну инструкцию, печатать следующую и проверять, что eip != 0x. Скрипт выглядит следующим образом: b *0x. Поместим скрипт в файл script. После выполнения получим следующее: В коде бросаются в глаза два вызова cpuid, после которых происходит зависание. Похоже, что это какие- то проверки. Разберемся что же они проверяют. Первый вызов выполняется с параметром eax = 0x. Далее значение сравнивается с 0x. Второй вызов выполняется с параметром eax = 0x. Long Mode. Похоже, что виртуальная машина зависает потому, что QEMU, которое я запускаю, не поддерживает Long Mode. Запустим виртуальную машину следующим образом: Ура, нам удалось запустить задание! Дело осталось за небольшим – выполнить его! Вообще, описанной выше проблемы с зависанием не случилось бы, если linux, в котором мы работаем, был 6. В данном случае нам просто не повезло с разрядностью системы. Подбор пароля. Добравшись до самого задания, становится ясно, что требуется сделать. Судя по всему, нужно подобрать “password”, который бы удовлетворял алгоритм проверки. Для этого нужно найти место, в котором происходит проверка введенного пароля. Постараемся остановить выполнение виртуальной машины как можно ближе к проверке пароля. Можно заметить, что перед выводом сообщения об ошибке, на четвертой строке печатается введенный пароль. Скорее всего, к этому моменту проверка еще не выполнена, так что, вывалившись в отладчик в этом месте, мы окажемся с уже введенным паролем, еще не прошедшим проверку. Останется подняться вверх по стеку до функции, в которой вывод пароля на экран и его проверка вызываются последовательно. Как же определить, где поставить брейкпоинт в коде, для того, чтобы попасть в нужное место? Печать символа на экран возможна двумя способами: Простой способ — запись символа в видеопамять по адресу 0xb. Сложный способ — написание драйвера, который бы настраивал видеокарту, и предоставлял функцию рисования точки на экране, а затем, используя шрифты, по точкам нарисовать символ. Вместо драйвера можно использовать BIOS VBE, как это сделано здесь. Предположим, что использовался простой способ. Тогда мы можем поставить брейкпоинт на доступ к видеопамяти, а именно, к первому символу четвертой строки. Видеопамять начинается с адреса 0xb. Команда на установку брейкпоинта на запись в память в gdb будет выглядеть так: watch *0xb. Предположение было верно, мы вывалились сразу после записи символа в память. Брейкпоинт больше не нужен, можно его удалить. Сделаем еще одно предположение – допустим, что есть функция, в которой последовательно вызывается код печати и проверки пароля. Тогда исходный код должен выглядеть следующим образом: Наша цель – найти функцию Check. Pass(). Для этого будем ставить брейкпоинты на адреса возврата из функций, вложенных в Print. Pass(), и продолжать выполнение. Если вывалились в только что установленный брейкпоинт, а сообщение “ Password incorrect.” еще не напечатано, то ставим новый и продолжаем. Если напечатано, то предпоследний поставленный и был нам нужен – он стоял сразу после вызова Print. Pass() в теле task(). Разберемся, как достать адрес возврата. Если код компилировался без специфичных флагов, то в начале функции парой инструкций “push $rbp; mov $rsp, $rbp” формируется новый фрейм стека. В таком случае, по адресу $rbp+8 хранится адрес возврата. Это легко проверить: Действительно, перед адресом 0xfffff. Теперь мы можем осуществить задуманное. Пишем скрипт. Так как неизвестно, какой глубины стек, напишем небольшой скрипт для gdb, который будет подниматься вверх по стеку, пока QEMU не начнет печатать на экран “Password incorrect”. Получим следующее: Скрипт завис, так и не попав в брейкпоинт по адресу 0x. Password incorrect” напечатано. Это значит, что предположение о существовании функции, которую мы назвали task(), верно. Зависание говорит о том, что функция task() никогда не возвращается после того, как сообщение “Password incorrect” печатается на экран. Впрочем, это не важно, главное, что мы теперь знаем адрес возврата из функции Print. Pass(), на который мы поставили предпоследний, тринадцатый брейкпоинт. Поиски продолжаются.. Снимем дамп кода, отступив несколько байт назад от RIP, чтобы узнать адрес функции, из которой мы только что вышли. Дизассемблируем полученный дамп командой “objdump –D –b binary –m i. Обратим внимание на то, что мы только что вышли из функции по адресу 0xfffff. Мы только что вышли из функции, которая, как минимум, вывела на экран текст, и эта функция вызывается несколько раз. В первый раз она вызывается с аргументами 0xfffff. Посмотрим, что находится по этим адресам. Функция 0xfffff. 80. Строка «1. 23» — это введенный пароль. Посмотрим, в зависимости от чего выводятся сообщения. Посмотрим, есть ли обращения к этой структуре в рассматриваемой функции. Если обратиться к коду этой функции, то станет ясно, что это memset. В структуру дважды записывается указатель на строку с введенным паролем, по смещению 0 и 3. Видимо, это инициализация. Если посмотреть на код между инициализацией и проверкой результата, то мы увидим следующее: ; выше находится инициализация структуры g. Несколько странно выглядит конструкция push/ret посреди кода, так как не ясно, как будет продолжаться выполнение после нее. Мы по- прежнему ищем функцию проверки пароля. Функции по адресам 0xfffff. Интерес представляет пара инструкций push, retq, посредством которых происходит прыжок на адрес 0xfffff.
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |