BinaryScan.pm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. # $OpenBSD: BinaryScan.pm,v 1.5 2014/07/09 11:26:11 espie Exp $
  2. # Copyright (c) 2011 Marc Espie <espie@openbsd.org>
  3. #
  4. # Permission to use, copy, modify, and distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. use strict;
  16. use warnings;
  17. # scan binaries through objdump or ldd, and record the results
  18. # - retrieves files through source (see FileSource)
  19. # - pass the result off to a recorder
  20. # Public interface is mostly:
  21. # set_source, retrieve_and_scan_binary, dont_scan, finish_scanning
  22. # it needs a source set to operate
  23. # it uses $state to display errors and to access the recorder
  24. package OpenBSD::BinaryScan;
  25. sub new
  26. {
  27. my ($class, $state) = @_;
  28. bless {state => $state}, $class;
  29. }
  30. sub set_source
  31. {
  32. my ($self, $source) = @_;
  33. $self->{source} = $source;
  34. }
  35. sub fatal
  36. {
  37. my $self = shift;
  38. $self->{state}->fatal(@_);
  39. }
  40. sub logger
  41. {
  42. my $self = shift;
  43. return $self->{state}{logger};
  44. }
  45. sub dest
  46. {
  47. my $self = shift;
  48. return $self->{state}{dump};
  49. }
  50. sub start
  51. {
  52. my ($self, @names) = @_;
  53. unless (open(my $cmd, '-|')) {
  54. if ($self->logger) {
  55. my $log = $self->logger->log($self->command.".err");
  56. open(STDERR, '>>', $log) or
  57. $self->fatal("Can't redirect: #1 #2", $log, $!);
  58. } else {
  59. open(STDERR, '>', '/dev/null');
  60. }
  61. chdir($self->{source}->directory) or
  62. $self->fatal("Bad directory #1: #2",
  63. $self->{source}->directory, $!);
  64. $self->exec(@names) or
  65. $self->fatal("exec #1: #2", $self->command, $!);
  66. } else {
  67. return $cmd;
  68. }
  69. }
  70. sub record_libs
  71. {
  72. my ($self, $fullname, @libs) = @_;
  73. my $fh;
  74. if (defined $fullname && defined $self->logger) {
  75. $fh = $self->logger->open("$fullname.log") or die "$!";
  76. print $fh "Libraries: ";
  77. }
  78. for my $lib (@libs) {
  79. # don't look for modules
  80. next if $lib =~ m/\.so$/;
  81. $self->dest->record($lib, $fullname);
  82. if (defined $fh) {
  83. print $fh "$lib ";
  84. }
  85. }
  86. if (defined $fh) {
  87. print $fh "\n";
  88. }
  89. }
  90. sub retrieve_and_scan_binary
  91. {
  92. my ($self, $item, $fullname) = @_;
  93. my $n = $self->{source}->retrieve($self->{state}, $item);
  94. # make sure to turn into a relative path
  95. $n =~ s/^\/*//;
  96. $self->scan_binary($item, $fullname, $n);
  97. }
  98. sub finish_retrieve_and_scan
  99. {
  100. my ($self, $item, $o) = @_;
  101. $o->{name} = $item->fullname;
  102. $o->create;
  103. my $n = $item->fullname;
  104. $n =~ s/^\/*//;
  105. $self->scan_binary($item, File::Spec->canonpath($item->fullname), $n);
  106. }
  107. sub dont_scan
  108. {
  109. my ($self, $item) = @_;
  110. $self->{source}->skip($item);
  111. }
  112. package OpenBSD::BinaryScan::Objdump;
  113. our @ISA = qw(OpenBSD::BinaryScan);
  114. sub command() { 'objdump' }
  115. sub exec
  116. {
  117. my ($self, @names) = @_;
  118. exec($self->command, '-p', @names);
  119. }
  120. sub parse
  121. {
  122. my ($self, $cmd, $names) = @_;
  123. my $fullname;
  124. my @l = ();
  125. my $linux_binary = 0;
  126. my $fh;
  127. if ($self->logger) {
  128. $fh = $self->logger->open("objdump.out");
  129. }
  130. while (my $line = <$cmd>) {
  131. if ($fh) {
  132. print $fh $line;
  133. }
  134. chomp $line;
  135. if ($line =~ m/^(.*)\:\s+file format/) {
  136. my $k = $1;
  137. $self->record_libs($fullname, @l) unless $linux_binary;
  138. $linux_binary = 0;
  139. @l = ();
  140. if (defined $names->{$k}) {
  141. $fullname = $names->{$k};
  142. }
  143. }
  144. if ($line =~ m/^\s+NEEDED\s+(.*?)\s*$/) {
  145. my $lib = $1;
  146. push(@l, $lib);
  147. # detect linux binaries
  148. if ($lib eq 'libc.so.6') {
  149. $linux_binary = 1;
  150. }
  151. } elsif ($line =~ m/^\s+RPATH\s+(.*)\s*$/) {
  152. my $p = {};
  153. for my $path (split /\:/, $1) {
  154. next if $path eq '/usr/local/lib';
  155. next if $path eq '/usr/X11R6/lib';
  156. next if $path eq '/usr/lib';
  157. $p->{$path} = 1;
  158. }
  159. my $d;
  160. if ($self->logger) {
  161. $d = $self->logger->open("$fullname.log");
  162. print $d "rpath: ";
  163. }
  164. for my $path (keys %$p) {
  165. $self->dest->record_rpath($path, $fullname);
  166. print $d "$path " if $d;
  167. }
  168. print $d "\n" if $d;
  169. }
  170. }
  171. $self->record_libs($fullname, @l) unless $linux_binary;
  172. }
  173. sub scan_binary
  174. {
  175. my ($self, $item, $fullname, $n) = @_;
  176. # okay, so we don't scan now, we keep it for later !
  177. $self->{names}{$n} = $fullname;
  178. push(@{$self->{cleanup}}, $item);
  179. if (@{$self->{cleanup}} >= 1000) {
  180. $self->finish_scanning;
  181. }
  182. }
  183. sub finish_scanning
  184. {
  185. my $self = shift;
  186. if (defined $self->{names}) {
  187. my $cmd = $self->start(sort keys %{$self->{names}});
  188. $self->parse($cmd, $self->{names});
  189. close($cmd);
  190. delete $self->{names};
  191. }
  192. if (defined $self->{cleanup}) {
  193. for my $item (@{$self->{cleanup}}) {
  194. $self->{source}->clean($item);
  195. }
  196. delete $self->{cleanup};
  197. }
  198. }
  199. package OpenBSD::BinaryScan::Ldd;
  200. our @ISA = qw(OpenBSD::BinaryScan);
  201. sub command() { 'ldd' }
  202. sub exec
  203. {
  204. my ($self, @names) = @_;
  205. if (@names > 1) {
  206. $self->fatal("Can't run ldd over several names");
  207. }
  208. exec($self->command, '-f', "NEEDED lib%o.so.%m.%n\n", @names);
  209. }
  210. sub parse
  211. {
  212. my ($self, $cmd, $fullname) = @_;
  213. my @l = ();
  214. my $linux_binary = 0;
  215. while (my $line = <$cmd>) {
  216. chomp;
  217. if ($line =~ m/^\s+NEEDED\s+(.*?)\s*$/) {
  218. my $lib = $1;
  219. push(@l, $lib);
  220. # detect linux binaries
  221. if ($lib eq 'libc.so.6') {
  222. $linux_binary = 1;
  223. }
  224. }
  225. }
  226. $self->record_libs($fullname, @l) unless $linux_binary;
  227. }
  228. sub scan_binary
  229. {
  230. my ($self, $item, $fullname, $n) = @_;
  231. my $cmd = $self->start($n);
  232. $self->parse($cmd, $fullname);
  233. close($cmd);
  234. $self->{source}->clean($item);
  235. }
  236. sub finish_scanning
  237. {
  238. }
  239. 1;