Отпечаток пальца на уровне сборки - PullRequest
8 голосов
/ 02 сентября 2011

Я хотел бы определить, были ли две функции в двух исполняемых файлах скомпилированы из одного и того же (C) исходного кода, и хотел бы это сделать, даже если они были скомпилированы разными версиями компилятора или с разными параметрами компиляции. В настоящее время я рассматриваю возможность внедрения какой-либо функции на уровне ассемблера. Отпечаток функции должен иметь свойства, которые:

  1. две функции, скомпилированные из одного и того же источника при разных обстоятельствах, могут иметь один и тот же отпечаток пальца (или похожую)
  2. две функции, скомпилированные из разных источников C, могут иметь разные отпечатки пальцев,
  3. (бонус), если две функции источника были похожи, отпечатки пальцев также будут похожи (для некоторого разумного определения сходства).

То, что я сейчас ищу, - это набор свойств скомпилированных функций, которые по отдельности удовлетворяют (1.) и, возможно, вместе взятые (2.).

Предположения

Конечно, это, как правило, невозможно, но может существовать что-то, что будет работать в большинстве случаев. Вот некоторые предположения, которые могут сделать это проще:

  • бинарные файлы linux ELF (но без информации об отладке),
  • никак не запутывается,
  • скомпилировано gcc,
  • на x86 linux (подход, который может быть реализован на других архитектурах, был бы неплох).

Идеи

К сожалению, у меня практически нет опыта сборки. Вот несколько идей для вышеупомянутых свойств:

  • типы инструкций, содержащихся в функции (то есть инструкции с плавающей запятой, барьеры памяти)
  • доступ к памяти из функции (она читает / записывает из / в кучу? Стек?)
  • вызываемые библиотечные функции (их имена должны быть доступны в ELF; их порядок обычно не должен меняться)
  • форма графа потока управления (я думаю, это будет сильно зависеть от компилятора)

Существующая работа

Мне удалось найти только косвенно связанную работу:


Есть ли у вас какие-либо предложения относительно свойств функции? Или другая идея, которая также выполняет мою цель? Или что-то подобное уже реализовано, и я полностью пропустил это?

Ответы [ 4 ]

5 голосов
/ 02 сентября 2011

FLIRT использует сопоставление с шаблоном байтового уровня, поэтому оно ломается при любых изменениях в кодировках команд (например, при различном распределении регистров / переупорядоченных командах).

Соответствие графику см. В BinDiff. Хотя это закрытый источник, Халвар описал некоторые подходы на его блоге . Они даже открыли исходный код некоторых из своих алгоритмов для создания отпечатков пальцев в виде плагина BinCrowd .

0 голосов
/ 02 сентября 2011

Если набор проблем может быть уменьшен до небольшого набора известных C или C++ функций исходного кода, компилируемых с помощью n различных компиляторов, каждая с m [n] различные наборы опций компилятора, тогда простым, но утомительным решением было бы скомпилировать код с каждой комбинацией компилятора и опций и каталогизировать результирующие байты инструкций или, что более эффективно, их хэш-сигнатуру в базы данных.

Набор вероятных используемых опций компилятора потенциально велик, но на практике инженеры обычно используют довольно стандартный и небольшой набор опций, обычно просто минимально оптимизированных для отладки и полностью оптимизированных для выпуска. Изучение многих конфигураций проекта может выявить, что в любой инженерной культуре есть только два или три, связанные с предубеждением или суеверием о том, как работают компиляторы - точные или нет.

Я подозреваю, что этот подход наиболее близок к тому, что вы на самом деле хотите: способ расследования предполагаемого незаконно присвоенного исходного кода. Все предлагаемые методы восстановления дерева разбора компилятора могут принести свои плоды, но имеют большой потенциал для игнорируемых симметричных решений или неоднозначных неразрешимых случаев.

0 голосов
/ 02 сентября 2011

Я предлагаю вам подойти к этой проблеме с точки зрения языка, на котором был написан код, и каких ограничений этот код накладывает на оптимизацию компилятора.

Я не очень знаком со стандартом C, но в C ++ есть понятие "наблюдаемого" поведения. Стандарт тщательно определяет это, и компиляторам дается большая свобода в оптимизации, если результат дает такое же наблюдаемое поведение. Моя рекомендация попытаться определить, являются ли две функции одинаковыми, состоит в том, чтобы попытаться определить, каково их наблюдаемое поведение (какой ввод / вывод они выполняют и как взаимодействуют с другими областями памяти и в каком порядке).

0 голосов
/ 02 сентября 2011

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

Это предотвратит переупорядочение команд, подъем цикла, развертывание цикла и любые другие оптимизации, мешающие сравнению, вы можете даже (де) оптимизировать структуры более высокого уровня до максимума на обоих концахчтобы убедиться, что они находятся на одном уровне, поэтому сравнение между неоптимизированным отладочным кодом и -O3 не будет неудачным из-за отсутствующих временных данных / отсутствия разливов регистра и т. д.

Вы можете использовать что-то вроде бумеранг в качестве основы для декомпиляции (кроме случаев, когда вы не выплюнете код C).

...