Как я делал CTF таск на WebAssembly
На сегодняшний день веб стал намного больше, чем обмен фалами по FTP или HTML страницы, доступные только для чтения. Сегодня браузер может заменить огромное количество приложений, и в большинстве случаев это стало возможно благодаря технологии, которая позволяет скомпилировать десктопные приложения под web архитектуру.
Технология называется WebAssembly и с ее помощью вы можете сделать свое десктопное приложение доступным для использования в интернете. На сегодняшний день существует компилятор emscripten, который позволяет скомпилировать C++ код в набор wasm инструкций.
Мне стало интересно, можно ли применить данную технологию для создания наглядного обучающего стенда для реверс-инжиниринга. Моя задумка заключалась в том, что после компиляции и запуска программы будет доступен интерфейс, в котором пользователь сможет просмотреть asm инструкции и значения в регистрах. С каждым следующим шагом пользователь будет двигаться по инструкциям и восстанавливать алгоритм работы программы. Основным отличием от полноценного дебаггера должен был стать упрощенный интерактивный интерфейс. Планировалось превратить реверс в подобие игры-головоломки, где при удачном решении задачи игрок получает флаг.
В ходе реализации своей задумки я столкнулся с проблемой, что webassembly имеет свой собственный набор инструкций. Данная особенность сделала невозможным отображение привычных asm команд и чтение значений в регистрах. Однако я могу обращаться к значениям по их поинтерам. Это заставило меня пересмотреть мою изначальную задумку.
В ходе размышлений я придумал интересную механику, в которой пользователю дается игровое поле с рядом неизвестных значений. Игрок может подсчитать адреса, где лежат эти значения, что дает ему преимущество в игровом процессе. Для моей задумки идеально подошла игра «сапер». В игре присутствует поле 10 на 10 ячеек, игрок может открывать ячейки самостоятельно, рискуя напороться на мину, а может открыть ячейку безопасным способом прочитав значение в ячейке по указателю. Однако игрок не знает адреса всех ячеек, а значит, ему необходимо найти (подсчитать) интересующий его адрес.
Прохождение данного таска я показал на своем YouTube канале.
Ознакомиться с таском можно в моем ТГ.
С какими сложностями я столкнулся
Выше я написал, что вы можете скомпилировать свой десктопный продукт под web. На самом деле это не совсем так. Для запуска «десктопного кода» нужно учитывать некоторые особенности браузера. Расскажу на примере своей игры.
Поскольку мы хотим сделать игру с графическим интерфейсом, встал вопрос выбора библиотеки для работы с окнами. Как оказалось, на сегодняшний день не все библиотеки можно скомпилировать при помощи emscripten. Я решил пойти путем наименьшего сопротивления и выбрал библиотеку SDL.
Весь игровой процесс происходит в бесконечном цикле. Если мы просто создадим бесконечный цикл и запустим его в браузере, скорее всего, страница с игрой зависнет. Для решения этой проблемы emscripten предоставляет функцию emscripten_set_main_loop_arg()
, на вход которой можно передать функцию с логикой игры.
Следующая сложность пришла откуда не ждали. В игре я использую спрайты, которые необходимо подгружать. SDL позволяет загружать изображения передав путь к файлу. SDL_Surface *surface = IMG_Load("assets/tiles.bmp");
Однако после компиляции я не получил желаемый результат. Оказалось, что при компиляции необходимо указать ряд расширений, которые мы хотим использовать в проекте. -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="[""bmp""]"
Обратите внимание на количество двойных кавычек. Если вы используете Windows это важно.
В интерфейсе помимо игрового поля я использую html формы для отправки данных в саму игру. Для такого взаимодействия понадобилось экспортировать некоторые функции во время компиляции.
-s EXPORTED_FUNCTIONS="['_myFunction', '_main']"
На этом моменте я столкнулся с самой неожиданной проблемой. Игровое поле (canvas) получает фокус, из за этого ввод с клавиатуры недоступен в html полях. Решение, которое я нашел, выглядит следующим образом. После компиляции в папке с проектом лежит js файл. Необходимо найти условие if (getWasmTableEntry(callbackfunc)(eventTypeId, keyEventData, userData))
и немного изменить его.
js файл до и после
После такого изменения canvas с игровым полем будет терять фокус после наведения мышки на html формы и ввод станет доступным.
Выводы
Описанная технология открывает большие возможности для разработчиков программного обеспечения. Предполагаю, что WebAssembly может стать отличным инструментом в борьбе с пиратством или презентации демо функционала своего продукта для конечного потребителя.
На сегодняшний день я ищу способы реализовать свою первоначальную задумку с симуляцией дебаггера.