Сравнение сгенерированных исполняемых файлов на соответствие

Мне нужно сравнить 2 исполняемых файла и / или общие объекты, скомпилированные с использованием одного и того же компилятора / флагов, и убедиться, что они не изменились. Мы работаем в регулируемой среде, поэтому было бы очень полезно для целей тестирования определить, какие именно части исполняемого файла изменились.

Использование MD5Sums/Hashes не работает из-за заголовков, содержащих информацию о файле.

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

4 ответа

Интересный вопрос У меня похожая проблема на Linux. Системы обнаружения вторжений, такие как OSSEC или tripwire, могут генерировать ложные срабатывания, если хэш-сумма исполняемого файла внезапно изменяется. Это может быть ничем не хуже, чем программа Linux "prelink", исправляющая исполняемый файл для более быстрого запуска.

Для сравнения двух двоичных файлов (в формате ELF) можно использовать исполняемый файл "readelf", а затем "diff" для сравнения выходных данных. Я уверен, что есть изысканные решения, но без лишних слов, компаратор бедняков в Perl:

#!/usr/bin/perl -w

$exe = $ARGV[0];

if (!$exe) {
   die "Please give name of executable\n"
}
if (! -f $exe) {
   die "Executable $exe not found or not a file\n";
}
if (! (`file '$exe'` =~ /\bELF\b.*?\bexecutable\b/)) {
   die "file command says '$exe' is not an ELF executable\n";
}

# Identify sections in ELF

@lines = pipeIt("readelf --wide --section-headers '$exe'");

@sections = ();

for my $line (@lines) {
   if ($line =~ /^\s*\[\s*(\d+)\s*\]\s+(\S+)/) {
      my $secnum = $1;
      my $secnam = $2;
      print "Found section $1 named $2\n";
      push @sections, $secnam;
   }
}

# Dump file header

@lines = pipeIt("readelf --file-header --wide '$exe'");
print @lines;

# Dump all interesting section headers

@lines = pipeIt("readelf --all --wide '$exe'");
print @lines;

# Dump individual sections as hexdump

for my $section (@sections) {
   @lines = pipeIt("readelf --hex-dump='$section' --wide '$exe'");
   print @lines;
}

sub pipeIt {
   my($cmd) = @_;
   my $fh;
   open ($fh,"$cmd |") or die "Could not open pipe from command '$cmd': $!\n";
   my @lines = <$fh>;
   close $fh or die "Could not close pipe to command '$cmd': $!\n";
   return @lines;
}

Теперь вы можете запустить, например, на машине 1:

./checkexe.pl /usr/bin/curl > curl_machine1

И на машине 2:

./checkexe.pl /usr/bin/curl > curl_machine2

После того, как вы скопировали, SFTP-ed или NSF-ed (вы не используете FTP, не так ли?) Файлы в одно и то же файловое дерево, сравните файлы:

diff --side-by-side --width=200 curl_machine1 curl_machine2 | less

В моем случае различия существуют в разделе ".gnu.conflict", ".gnu.liblist", ".got.plt" и ".dynbss", что может быть хорошо для вмешательства "prelink", но в разделе кода ".text", что было бы плохим знаком.

Чтобы продолжить, вот что я наконец-то придумал:

Вместо того чтобы сравнивать конечные исполняемые файлы и общие объекты, мы сравнивали выходные файлы.o перед компоновкой. Мы предполагали, что процесс связывания достаточно воспроизводим, чтобы это было нормально.

Это работает в некоторых из наших случаев, когда у нас есть две сборки, в которые мы внесли небольшое изменение, которое не должно влиять на окончательный код (Code pretty-printer), но не помогает нам, если у нас нет промежуточного результата сборки,

Вы можете сравнить содержимое инициализированных разделов RO и RW, создав двоичный файл из файла ELF.

objcopy <elf_file> -O binary <binary_file>

Используйте сгенерированные двоичные файлы для сравнения, если они идентичны, используя diff, например.

На мой взгляд, этого достаточно, чтобы предоставить вам тот же исполняемый файл.

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

Я помню, что есть некоторые временные метки в исполняемом файле. Хитрость заключается в том, чтобы понять, что файл - это не просто набор байтов, который не может быть интерпретирован. В файле есть разделы, большинство не изменится, но будет раздел для времени сборки (или что-то подобное).

Я не помню всех деталей, но вам понадобятся следующие команды: { objcopy, objdump, nm }, я думаю, что objdump будет первым, кто попробует.

Надеюсь это поможет.

Другие вопросы по тегам