Райтап по заданию No way out (PicoCTF)
Сегодня я хотел бы рассмотреть одно из заданий на реверс-инжиниринг с сайта PicoCTF, которое мне показалось достаточно любопытным, чтобы написать его полноценный разбор.
Вопросы и замечания направлять сюда (Telegram): @visilii или сюда (e-mail): workregor собака mail тчк ru

Скачав архив по ссылке, нам предстает необычное зрелище - вместо консольной программки, требующей пароль, или простенького GUI-приложения с текстовым полем и кнопкой мы видим… игру на Unity:

Запустим её. Мы оказываемся в помещении без потолка с несколькими кирпичными кубами одинаковых размеров и лестницей, ведущей на платформу (это будет важно далее). За пределами коробки видна область с огромным флагштоком - очевидно, чтобы получить флаг, нам нужно туда попасть. Но перед нами встаёт любимый приём ленивых разработчиков игр - невидимая стена, не дающая нам выбраться!

Итак, очевидно, что нам как-то нужно обойти ограничение, поставленное разработчиком. Очевидный способ - каким-то образом изменить координаты игрока так, чтобы оказаться за пределами комнаты.
Не знаю, как вы, а я сразу подумал о Cheat Engine. Этот дебаггер/сканер позволяет довольно легко находить и изменять игровые значения в памяти процесса. В этом райтапе я коснусь лишь основ этой программы - это очень мощный инструмент и в Интернете можно найти довольно много материалов по нему. Только не пытайтесь его использовать в мультиплеерных играх и помните - читерить плохо ;) Советую пройти встроенный туториал, он содержит бóльшую часть того, что я покажу здесь, и даже больше.
Откроем Cheat Engine и присоединим его к процессу нашей игры:

Поскольку мы имеем дело не с тайловой игрой типа классических рогаликов, наши координаты - это, скорее всего, числа с плавающей точкой. Значит, найдём все числа с плавающей точкой в памяти процесса.
Тут стоит пояснить, чего мы в конечном счёте пытаемся добиться. Нам необходимо найти конкретный участок памяти, где хранятся координаты игрока. В памяти процесса миллионы разных чисел, и чтобы отсеять ненужные и оставить интересные нам, мы должны предсказуемым образом менять значение в игре и просить Cheat Engine отсеять те числа, что не соответствуют этим изменениям. Проще показать это на примере:
Допустим, мы хотим изменить количество очков здоровья в какой-то игре. Мы знаем, что в данный момент у игрока 100 очков, поэтому ищем все числа, в данный момент равные 100. Их, скорее всего, очень много, поэтому после этого мы получаем урон и наше здоровье падает до 72. В Cheat Engine мы ищем среди ранее найденных значений те, что упали со 100 до 72. Cheat Engine способен автоматически отсеять не подходящие под критерий числа, нам достаточно указать новое значение и пересканировать. После этого можно попробовать вылечить здоровье и найти в имеющейся выборке числа, которые выросли в значении. Подобное продолжается до тех пор, пока у нас не остается ограниченный набор чисел (в идеале - одно), которое мы затем можем пытаться изменить, чтобы поменять здоровье игрока в игре.
Какое значение мы можем предсказуемо поменять? Нас интересуют координаты, но мы не знаем, в какую сторону положительное направление координат X/Z, а в какую - отрицательное. Но мы точно знаем, где верх, а где низ! Вот где нам пригодятся разные возвышенности внутри комнаты.
Для начала в Cheat Engine мы выбираем “Неизвестное начальное значение” (мы не знаем, на какой высоте изначально находимся) типа “float”.


Cheat Engine нашёл почти 218 млн. дробных чисел. Теперь заберёмся повыше - например, вскарабкаемся на середину лестницы - и просканируем все значения, которые выросли.

Красные числа в колонке Value означают, что число изменилось со времени последнего сканирования.
Выборка значительно сократилась - с 218 млн. до полутора. Немного подождём, повертим камерой, но не будем двигать нашего игрока в пространстве, и на этот раз просканируем оставшиеся полтора миллиона чисел, чтобы найти те, что не менялись - поскольку мы не меняли нашу высоту, это должно отсеять всё лишнее.

Дальнейший процесс отсеивания заключается в том же - забираемся на возвышенность, выбираем Increased value, ходим по ровной поверхности, не спускаясь и не поднимаясь, выбираем Unchanged value, спускаемся вниз - выбираем Decreased value. В конечном итоге у нас остаётся около 20 значений - и теперь мы уже можем вручную определить нужное нам.

Выбираем все значения, ПКМ > Add selected addresses to the addresslist. Теперь, в списке адресов последовательно меняем каждое значение и смотрим, изменяется ли положение игрока. Я уменьшал каждое значение, предварительно забравшись на платформу, чтобы оказаться под ней, а не под всей игровой картой в случае успеха. Важно менять значения осторожно, чтобы не улететь в космос!

Мы нашли координату Y, но нам нужно оказаться за пределами комнаты, а не над/под ней! Удалим остальные переменные из списка адресов и откроем участок памяти, где хранится наша координата Y (ПКМ > Browse this memory region):


Нас встречает hex viewer, и наше значение координаты Y расположено в самом начале показанной области памяти в нижней половине окна. Здесь красным фоном подсвечиваются значения, изменившиеся в течение последней секунды. Я изменю это время на 7 секунд (ПКМ > Change fade timer) - так проще увидеть, что поменялось, совершив действия в игре.
Подпрыгнув и быстро заглянув в Memory Viewer, я вижу, что участок, где хранится наша координата, был изменён.

Теперь найдем таким же образом, где хранится одна из горизонтальных координат. Для этого я упрусь в стену и, не двигая мышью и не делая других движений, пойду вдоль стены вперёд.
Как видим, участок из 8 байт (с 98 по 9F) загорелся красным - скорее всего, это и есть наша координата X (или Z). Не обращайте внимания на красные значения ниже - они меняются несколько раз в секунду и не зависят от движений игрока:

Всё, что нам осталось - это аккуратно изменить это значение, чтобы оказаться за пределами карты. Числовые значения, включая числа с плавающей точкой, хранятся в памяти в LE (Little Endian)-формате, что означает, что старшие байты числа идут после младших разрядов. Попробуем поменять предпоследний (т.е. второй по старшинству) байт:

И оказываемся на свободе! Теперь нам осталось лишь подойти к флагштоку, чтобы увидеть наш заветный флаг :)
На мой взгляд, это очень остроумное задание, и я был рад вновь пощупать Cheat Engine. Есть что-то волшебное в том, чтобы поменять одно число в памяти и получить реальный, ощутимый эффект внутри игры.
Я постараюсь публиковать больше райтапов по тем задачкам, которые показались мне интересными и познавательными. А пока - до новых встреч!
Теги: инфобез, reverse-engineering