比较生成的可执行文件是否相等

9
我需要比较两个可执行文件和/或共享对象,它们是使用相同的编译器/标志编译的,并验证它们没有发生更改。我们在受监管的环境中工作,因此对于测试目的,能够隔离可执行文件的哪些部分发生了变化将非常有用。
使用MD5校验和/哈希值无法正常工作,因为头信息包含有关文件的信息。
是否有人知道一种程序或方法来验证即使在不同时间构建,这两个文件的执行结果是否相同?

1
可能取决于平台... - skaffman
好的,这是使用GCC编译的Linux系统。 - Luciano
4个回答

5
一个有趣的问题。我在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

并在第二台机器上:

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

在将文件复制粘贴、通过SFTP或NSF传输(您不使用FTP,对吧?)到相同的文件树中后,请比较这些文件:
diff --side-by-side --width=200 curl_machine1 curl_machine2 | less

在我的情况下,".gnu.conflict"、".gnu.liblist"、".got.plt"和".dynbss"这些部分存在差异,这可能对于"prelink"干预来说是可以的,但在代码部分".text"中,这将是一个不好的迹象。

1

跟进一下,这是我最终想出来的方案:

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

在我们的某些情况下,它是有效的,例如我们有两个构建版本,我们对其中一个进行了一些小的更改(代码漂亮打印机),这不会影响最终的代码,但如果我们没有构建中间输出,则无法帮助我们。


0

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

objcopy <elf_file> -O binary <binary_file>

使用生成的二进制文件进行比较,例如使用 diff
在我看来,这已足以保证您生成的可执行文件相同。

-1
几年前我也曾经做过同样的事情。我们必须证明,当只给定修订号、修订控制存储库、构建工具和构建配置时,我们可以从源代码重新构建可执行文件。注意:如果其中任何一个发生变化,您可能会看到差异。
我记得可执行文件中有一些时间戳。诀窍在于意识到该文件不仅仅是一堆无法解释的字节。该文件有多个部分,大多数部分不会改变,但会有一个用于构建时间(或类似内容)的部分。
我不记得所有的细节,但您需要的命令是{ objcopy,objdump,nm },我认为首先尝试使用objdump
希望这可以帮助到您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接