User1
Спортпрог который мы заслужили @ch4nnel1
Когда-то (5-12 ноября) была олимпудка ломоносова по информатике (спортивное програмирование). Вот я её решаю, решаю, открываю очередное задание.
А оно начинается со слов: "Вам дан бинарный файл…".
Я такой: "Прикольно звучит как реверс, было бы забавно. Хе-хе".
Дальше : "… без исходного кода…".
- "Все чудесатее и чудесатее"
Продолжение читайте в источнике
Тут вообще должно было быть условие задания, но его нет(
Сам таск... А ссылки тоже нет(
Придется рассказать, если кратко: дан бинарный файл без исходного кода, сказано что нужно написать программу, которая по его выводу будет определять, что у него было на вводе. И так же сказано, что если Вы лох и не имеете под рукой линукс (бинарь дан под линукс), то существует какой-то jslinux и минимальная инструкция для него.
(а еще я вспомнил, что там вроде бы надо было фильтровать ввод, но это неточно и на соревах я про это успешно забыл).
Можем попробовать "поблекбоксить" таск, и поймем что скорее всего он принимает только чиселки, ведь на все остальное он возвращает "константный" ответ 1.
Но все таки надо начать реверс, ведь сомневаюсь что дали бы что-то ультрапростое. Открыл таск в иде, она все разреверсила. Получил, что там есть две функции: "encrypt" и "decrypt". Сначала число декриптится (байтовые сдвиги и ксор), потом к зашифрованному прибавляется 1, и это дело инкриптится (опять байтовые сдвиги и ксор).
Я подумал, что так это все слишком легко и пошел реверсить асм через радар хоть для минимального челенджа, но даже там все вышло ± легко. Единственное, что было интересненьким это работа с 8 байтным числом через 2 32 битных регистра (бинарь был 32 битным).
И тут мне в голову пришла "гениальная" мысль, а что если представить, что я не могу реверснуть эти функции, так как в них дана какая-нибудь мега ультра хардкор крипта, это же будет так весело чуть пострадать…
Ну и да, гора ошибок в будущем не заставила себя ждать.
Но начнем по порядку, моя идея заключалась в том чтобы в мою программу засунуть байты функций декода и инкода, пошаманить с памятью, сделать ее исполняемой и как-нибудь вызвать эти функции (и в теории эти функции должны работать так же, ведь сомневаюсь, что орги компилят под какой-то свой проц или делают еще что-то что должно помешать байтам исполниться так же как и у меня). Сделать что-то наподобие самоинжекта кода. Все звучит не слишком сложно если еще учитывать, что у меня оставались наработки для более комплексной штуки еще с лета.
Первым делом я проверил, что функции декода и инкода являются обратными: на все 10 вариантов, что я попробовал они сработали правильно.
Но дальше все оказалось не так радужно, первая же проблема с которой я столкнулся - бинарный файл был 32 битным и для вызова функций использовал соглашение cdecl (для меня это было проблемой так как я его просто несильно люблю).
(Тут я опущу как долго тупил и искал "правильный" компилятор в https://godbolt.org/).
После некоторого времени до меня дошло, что можно было бы воспользоваться тем же компилятором, которым была скомпилен данный мне файл. И тогда и функции должны будут вызываться полностью так же как и в данном мне файле. Недолго думая, с помощью любимого радара достал байты нужных функций, и написал прогу, которая должна была работать.
Даже сначала проверил в https://godbolt.org/, что особой разницы в вызовах функций у gcc версии 11.1.1 и 9.3.0 (которой компилировался данный бинарь/та что стоит у меня) нету. Дальше попытался ее скомпилить данной командой
С этим всем я тоже успел намучиться, оказывается gcc "умный" и если видит у файла на конце cpp, то пытается его компилить как c++, а для него у меня не установленны какие-то нужные 32 битные зависимости. В результате достаточно было переименовать файл в test.c и все скомпилилось довольно хорошо.
Код на данный момент:
Дальше запускаем, пробуем, что-то ввести, и оно падает
Я с подобным уже сталкивался, и тогда это было вызвано тем, что mprotect
умеет изменять права памяти, только если ему дано начало страницы (раньше у меня malloc выделял память в начале страницы. Поэтому сразу и не пофиксил). Сделаем небольшие изменения, скомпилим… И БАЦ, оно теперь не падает и даже возвращает верный результат.
Засылаем проверяющей системе, оно проходит один единственный тест -> скорее всего не падает, радуемся жизни.