【问题标题】:Comparing generated executables for equivilance比较生成可执行文件的等效性
【发布时间】:2011-03-14 23:36:17
【问题描述】:

我需要比较使用相同编译器/标志编译的 2 个可执行文件和/或共享对象,并验证它们没有更改。我们在受监管的环境中工作,因此对于测试目的而言,准确隔离可执行文件的哪些部分已更改非常有用。

使用 MD5Sums/Hashes 不起作用,因为标头包含有关文件的信息。

有没有人知道一个程序或方法来验证两个文件在执行上是否相同,即使它们是在不同时间构建的?

【问题讨论】:

  • 可能取决于平台......
  • 好的,它是用 GCC 编译的 Linux。

标签: comparison executable shared-objects binary-reproducibility


【解决方案1】:

一个有趣的问题。我在linux上有类似的问题。如果可执行文件的哈希值突然发生变化,OSSEC 或tripwire 等入侵检测系统可能会产生误报。这可能比 Linux“预链接”程序修补可执行文件以加快启动速度更糟糕。

为了比较两个二进制文件(在ELF format 中),可以使用“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”部分存在差异,这对于“预链接”干预可能是可以的,但在代码部分“.text”,这将是一个坏标志。

【讨论】:

    【解决方案2】:

    为了跟进,这是我最终想出的:

    我们没有比较最终的可执行文件和共享对象,而是比较了链接前的 .o 文件输出。我们假设链接过程具有足够的可重复性,这样就可以了。

    它适用于我们的某些情况,在我们有两个构建的情况下,我们做了一些不应该影响最终代码的小改动(Code pretty-printer),但如果我们没有构建中间输出。

    【讨论】:

      【解决方案3】:

      您可以通过从 ELF 文件生成二进制文件来比较 RO 和 RW 初始化部分的内容。

      objcopy <elf_file> -O binary <binary_file>
      

      使用生成的二进制文件比较它们是否相同,例如使用diff

      在我看来,这足以让您生成相同的可执行文件。

      【讨论】:

        【解决方案4】:

        几年前,我不得不做同样的事情。我们必须证明,当只给定修订号、修订控制存储库、构建工具和构建配置时,我们可以从源代码重建可执行文件。注意:如果其中任何发生变化,您可能会发现差异。

        我记得可执行文件中有一些时间戳。诀窍是要意识到文件不仅仅是一堆字节,无法解释。该文件有部分,大部分不会改变,但会有一个构建时间部分(或类似的东西)。

        我不记得所有细节,但您需要的命令是 { objcopy, objdump, nm },我认为 objdump 将是第一个尝试的。

        希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-30
          • 1970-01-01
          • 2015-11-01
          • 1970-01-01
          相关资源
          最近更新 更多