
Вырезаем 99% мусорного кода с помощью SSA в Binary Ninja (Flare-On 12)
farvend 15 минут назад Вырезаем 99% мусорного кода с помощью SSA в Binary Ninja (Flare-On 12) Сложный 11 мин 559 Реверс-инжиниринг * Информационная безопасность * Туториал ЗаманкаСкачать заданияМы из вот такой...
Anthropic — What company has the best second artificial intelligence model at the end of June?
Вот важная новость с фронта ИИ: farvend 15 минут назад Вырезаем 99% мусорного кода с помощью SSA в Binary Ninja (Flare-On 12) Сложный 11 мин 559 Реверс-инжиниринг * Информационная безопасность * Туториал ЗаманкаСкачать заданияМы из вот такой огроменной (это лишь малая её часть) функции, в ней 4185 (! ) строк декомпиляцииИзначальный mainПолучим вот этоДеобфусцированный mainА тут всего 35 строкВступление Проходя недавний Flare-On 12, я открыл 7 задание, нашел main и увидел следующую картину: Ужасная куча вычисленийИ ещё тысячи строк подобного бреда, а логики не видно. Так что весь этот мусор нужно как-то убрать.
Если посмотреть на граф потока управления, то видно, что он довольно простой (я его показывать не буду, так как он настолько длинный, что блоки там просто не видны), так что это не часть control flow flattening. Можно посмотреть все вызовы функций через>>> len(current_function. call_sites) 31(Я использую Binary Ninja, этот и последующие скрипты будут для него)Видим довольно скромное количество вызовов, значит, скорее всего, мусорных вызовов нет.
Технические детали
Если потыкать по этим вызовам, то можно убедиться, что параметры получаются только через другие вызовы напрямую. Вот что я имею ввиду:Вызовы функций, которые не используют тяжёлые вычисленияПолучается, весь этот гигантский пласт математики можно просто вырезать без каких-либо последствий для работы программы. Возникает логичный вопрос: если этот код ни на что не влияет, почему декомпилятор не выкинул его?
А ответ в том, что всё-таки эти вычисления влияют на глобальные переменные, но только чтоб это понять нужно полистать декомпиляцию. В мусорных кусках происходит примерно это:int64_t var1 = data_1001; // data_1001 нигде в полезном не используется # куча преобразований var1, может быть использования других переменных data_1004 = var1_new; // data_1004 нигде в полезном не используетсяТак что по-сути все эти вычисления могут влиять на поведение программы (запись в глобальные переменные), но вот только эти переменные скорее всего нужны только для запутывания кодаОднако я этот вывод сделал лишь на одном куске main, в других функциях и других кусках может быть эти вычисления всё-таки нужны. Подготавливаемся проверять гипотезуПолучается нам нужно доказать в main, что параметры вызовов функций зависят от малой части переменных main.
Один из самых стабильных и рабочих вариантов, который мне пришёл в голову, так это как-то отследить использования переменной в вызове функции до её создания, но как мы будем это делать? Перерыв на теориюМожно, конечно, парсить ассемблер, но это было бы ужасно сложно (перезаписи регистров, переменные относительно rbp и т. ), поэтому нам нужен инструмент, который : Предоставляет переменные, а не регистры Позволяет сказать как конкретное состояние переменной получилось.
Для этого прекрасно подходит MLIL из Binary Ninja в SSA форме! Давайте объясню что этоMLILВ Binary Ninja есть промежуточные представления, которые предназначены для лифтинга машинного кода в псевдо Си.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





