1# 2# Keyword search mode 3# 4# Brian Carrier [carrier@sleuthkit.org] 5# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved 6# 7# This file is part of the Autopsy Forensic Browser (Autopsy) 8# 9# Autopsy is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# Autopsy is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with Autopsy; if not, write to the Free Software 21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22# 23# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 24# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 25# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. 26# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR 29# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 34package Kwsrch; 35 36require 'search.pl'; 37 38$Kwsrch::ENTER = 1; 39$Kwsrch::RESULTS_FR = 2; 40$Kwsrch::RUN = 3; 41$Kwsrch::LOAD = 4; 42$Kwsrch::BLANK = 5; 43 44my $IMG_DETAILS = 0x80; 45 46sub main { 47 48 # By default, show the main frame 49 $Args::args{'view'} = $Args::enc_args{'view'} = $Kwsrch::ENTER 50 unless (exists $Args::args{'view'}); 51 52 Args::check_view(); 53 my $view = Args::get_view(); 54 55 if ($view == $Kwsrch::BLANK) { 56 blank(); 57 return 0; 58 } 59 60 # Check Basic Args 61 Args::check_vol('vol'); 62 63 # These windows don't need the meta data address 64 if ($view == $Kwsrch::ENTER) { 65 return enter(); 66 } 67 elsif ($view == $Kwsrch::RESULTS_FR) { 68 return results_fr(); 69 } 70 elsif ($view == $Kwsrch::RUN) { 71 return run(); 72 } 73 elsif ($view == $Kwsrch::LOAD) { 74 return load(); 75 } 76 else { 77 Print::print_check_err("Invalid Keyword Search View"); 78 } 79} 80 81my $CASE_INSENS = 1; 82my $CASE_SENS = 0; 83 84my $REG_EXP = 1; 85my $STRING = 0; 86 87# Form to enter search data 88sub enter { 89 my $vol = Args::get_vol('vol'); 90 91 Print::print_html_header("Search on $Caseman::vol2sname{$vol}"); 92 my $ftype = $Caseman::vol2ftype{$vol}; 93 94 if ($ftype eq 'blkls') { 95 print "<center><h3>Keyword Search of Unallocated Space</h3>\n"; 96 } 97 elsif ($ftype eq 'swap') { 98 print "<center><h3>Keyword Search of swap partition</h3>\n"; 99 } 100 elsif ($ftype eq 'raw') { 101 print "<center><h3>Keyword Search of raw data</h3>\n"; 102 } 103 elsif ($Caseman::vol2cat{$vol} eq "disk") { 104 print "<center><h3>Keyword Search of disk</h3>\n"; 105 } 106 else { 107 print 108"<center><h3>Keyword Search of Allocated and Unallocated Space</h3>\n"; 109 } 110 111 # @@@ Fix this - caused by writing all results to a file 112 if ($::LIVE == 1) { 113 Print::print_err( 114"Keyword searching is temporarily not available during live analysis mode" 115 ); 116 } 117 118 print "<form action=\"$::PROGNAME\" method=\"get\">\n" 119 . "Enter the keyword string or expression to search for:<br> <input type=\"text\" name=\"str\"><br><br>\n" 120 . Args::make_hidden() 121 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" 122 . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" 123 . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n"; 124 125 print "<table width=400><tr>\n" 126 . "<td width=200 align=center><input type=\"checkbox\" name=\"ascii\" value=\"1\" CHECKED>" 127 . "ASCII \n</td>" 128 . "<td width=200 align=center><input type=\"checkbox\" name=\"unicode\" value=\"1\" CHECKED>" 129 . "Unicode</td></tr>\n" 130 . "<tr><td align=center><input type=\"checkbox\" name=\"srch_case\" value=\"$CASE_INSENS\">" 131 . "Case Insensitive</td>\n" 132 . "<td align=center><input type=\"checkbox\" name=\"regexp\" value=\"$REG_EXP\">\n" 133 . "<tt>grep</tt> Regular Expression</td></tr></table>\n" 134 . "<input type=\"image\" src=\"pict/but_search.jpg\" " 135 . "alt=\"Search\" border=\"0\">\n</form>\n"; 136 137 if ($::LIVE == 0) { 138 print "<table width=600><tr>\n"; 139 140 # If we are a non-blkls image and one exists - make a button to load it 141 if (($ftype ne 'blkls') && (exists $Caseman::vol2blkls{$vol})) { 142 print "<td align=center width=200>" 143 . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" 144 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" 145 . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_KWSRCH\">\n" 146 . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::vol2blkls{$vol}\">\n" 147 . Args::make_hidden() 148 . "<input type=\"image\" src=\"pict/srch_b_lun.jpg\" " 149 . "alt=\"Load Unallocated Image\" border=\"0\">\n<br></form></td>\n"; 150 } 151 152 # If we are a blkls and the original exists - make a button to load it 153 elsif (($ftype eq 'blkls') 154 && (exists $Caseman::mod2vol{$vol})) 155 { 156 print "<td align=center width=200>" 157 . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" 158 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" 159 . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_KWSRCH\">\n" 160 . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::mod2vol{$vol}\">\n" 161 . Args::make_hidden() 162 . "<input type=\"image\" src=\"pict/srch_b_lorig.jpg\" " 163 . "alt=\"Load Original Image\" border=\"0\">\n<br></form></td>\n"; 164 } 165 166 # Strings Button 167 if ( (!(exists $Caseman::vol2str{$vol})) 168 || (!(exists $Caseman::vol2uni{$vol}))) 169 { 170 171 my $dest_vol = $vol; 172 $dest_vol = $Caseman::mod2vol{$vol} 173 if exists($Caseman::mod2vol{$vol}); 174 175 print "<td align=center width=200>" 176 . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" 177 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" 178 . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_DETAILS\">\n" 179 . "<input type=\"hidden\" name=\"vol\" value=\"$dest_vol\">\n" 180 . Args::make_hidden() 181 . "<input type=\"image\" src=\"pict/srch_b_str.jpg\" " 182 . "alt=\"Extract Strings\" border=\"0\">\n<br></form></td>\n"; 183 } 184 185 # Unallocated Space Button 186 if ( ($Fs::is_fs{$ftype}) 187 && (!(exists $Caseman::vol2blkls{$vol}))) 188 { 189 print "<td align=center width=200>" 190 . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" 191 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" 192 . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_DETAILS\">\n" 193 . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" 194 . Args::make_hidden() 195 . "<input type=\"image\" src=\"pict/srch_b_un.jpg\" " 196 . "alt=\"Extract Unallocated Space\" border=\"0\">\n<br></form></td>\n"; 197 } 198 199 print "</tr></table>\n"; 200 } 201 202 print "<a href=\"help/grep.html\" target=\"_blank\">" 203 . "Regular Expression Cheat Sheet</a>\n<br><br>\n"; 204 205 print "<p><font color=\"red\">NOTE:</font> The keyword search runs " 206 . "<tt>grep</tt> on the image.<br>\n" 207 . "A list of what will and " 208 . "what will not be found is available " 209 . "<a href=\"help/grep_lim.html\" target=\"_blank\">here</a>.<br>\n"; 210 211 # Section for previous searches 212 if ($::LIVE == 0) { 213 my $srch_name = get_srch_fname(0); 214 if (-e $srch_name) { 215 print "<hr><h3>Previous Searches</h3>\n" . "<table width=600>\n"; 216 my $row_idx = 0; 217 218 # Cycle through the files 219 for (my $srch_idx = 0;; $srch_idx++) { 220 221 $srch_name = get_srch_fname($srch_idx); 222 223 last unless (-e $srch_name); 224 225 # Open the file to get the string and count 226 unless (open(SRCH, "$srch_name")) { 227 print "Error opening search file: $srch_name\n"; 228 return 1; 229 } 230 my $prev_str = ""; 231 my $prev_cnt = 0; 232 233 while (<SRCH>) { 234 unless (/^(\d+)\|(.*?)?\|(.*)$/) { 235 print 236 "Error pasing header of search file: $srch_name\n"; 237 return 1; 238 } 239 $prev_cnt = $1; 240 $prev_str = $3; 241 if (length($prev_str) > 32) { 242 $prev_str = substr($prev_str, 0, 32); 243 $prev_str .= "..."; 244 } 245 246 last; 247 } 248 close(SRCH); 249 250 print "<tr>\n" if ($row_idx == 0); 251 252 print " <td align=center width=150>\n" 253 . "<form action=\"$::PROGNAME\" method=\"get\">\n" 254 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" 255 . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" 256 . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" 257 . "<input type=\"hidden\" name=\"srchidx\" value=\"$srch_idx\">\n" 258 . Args::make_hidden(); 259 260 print "<input type=\"SUBMIT\" value=\"".Print::html_encode($prev_str)." ($prev_cnt)\">" 261 . "<br></form>\n"; 262 263 if ($row_idx == 3) { 264 print "</tr>\n"; 265 $row_idx = 0; 266 } 267 else { 268 $row_idx++; 269 } 270 } 271 print "</table>\n"; 272 } 273 } 274 275 # Predefined expressions from search.pl 276 print "<hr><h3>Predefined Searches</h3>\n"; 277 print "<table width=600>\n"; 278 my $row_idx = 0; 279 my $r; 280 foreach $r (keys %Kwsrch::auto_srch) { 281 282 $Kwsrch::auto_srch_reg{$r} = 0 283 unless (defined $Kwsrch::auto_srch_reg{$r}); 284 $Kwsrch::auto_srch_csense{$r} = 1 285 unless (defined $Kwsrch::auto_srch_csense{$r}); 286 287 print "<tr>\n" if ($row_idx == 0); 288 289 # @@@ Make a unicode option in predefined 290 291 print " <td align=center width=150>\n" 292 . "<form action=\"$::PROGNAME\" method=\"get\">\n" 293 . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" 294 . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" 295 . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" 296 . "<input type=\"hidden\" name=\"str\" value=\"$Kwsrch::auto_srch{$r}\">\n" 297 . "<input type=\"hidden\" name=\"ascii\" value=\"1\">\n" 298 . Args::make_hidden(); 299 300 if ($Kwsrch::auto_srch_reg{$r} == 1) { 301 print 302 "<input type=\"hidden\" name=\"regexp\" value=\"$REG_EXP\">\n"; 303 } 304 if ($Kwsrch::auto_srch_csense{$r} == 0) { 305 print 306"<input type=\"hidden\" name=\"srch_case\" value=\"$CASE_INSENS\">\n"; 307 } 308 print "<input type=\"SUBMIT\" value=\"$r\"><br></form>\n" . " </td>\n"; 309 310 if ($row_idx == 3) { 311 print "</tr>\n"; 312 $row_idx = 0; 313 } 314 else { 315 $row_idx++; 316 } 317 } 318 print "</table>\n"; 319 320 Print::print_html_footer(); 321 return 0; 322} 323 324# MAIN WITH RESULTS 325# Page that makes frame with the results on left and data units on right 326sub results_fr { 327 my $vol = Args::get_vol('vol'); 328 329 # A string was given for a new search 330 if (exists $Args::args{'str'}) { 331 Args::check_str(); 332 333 Print::print_html_header_frameset( 334 "Search on $Caseman::vol2sname{$vol} for $Args::args{'str'}"); 335 336 print "<frameset cols=\"35%,65%\">\n"; 337 338 my $srch_case = ""; 339 $srch_case = "&srch_case=$Args::args{'srch_case'}" 340 if (exists $Args::args{'srch_case'}); 341 342 my $regexp = ""; 343 $regexp = "®exp=$Args::args{'regexp'}" 344 if (exists $Args::args{'regexp'}); 345 346 my $ascii = ""; 347 $ascii = "&ascii=$Args::args{'ascii'}" 348 if (exists $Args::args{'ascii'}); 349 350 my $unicode = ""; 351 $unicode = "&unicode=$Args::args{'unicode'}" 352 if (exists $Args::args{'unicode'}); 353 354 # Block List 355 print "<frame src=\"$::PROGNAME?" 356 . "mod=$::MOD_KWSRCH&view=$Kwsrch::RUN&" 357 . "$Args::baseargs$srch_case$regexp&str=$Args::enc_args{'str'}$ascii$unicode\">\n"; 358 } 359 elsif (exists $Args::args{'srchidx'}) { 360 Args::check_srchidx(); 361 362 Print::print_html_header_frameset( 363"Search on $Caseman::vol2sname{$vol} for Index $Args::args{'srchidx'}" 364 ); 365 366 print "<frameset cols=\"35%,65%\">\n"; 367 368 # Block List 369 print "<frame src=\"$::PROGNAME?" 370 . "mod=$::MOD_KWSRCH&view=$Kwsrch::LOAD&" 371 . "$Args::baseargs&srchidx=$Args::enc_args{'srchidx'}\">\n"; 372 } 373 374 # Block Contents 375 print "<frame src=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::BLANK&" 376 . "$Args::baseargs\" name=\"content\">\n" 377 . "</frameset>\n"; 378 379 Print::print_html_footer_frameset(); 380 return 0; 381} 382 383# Find an empty file to save the keyword searches to 384sub find_srch_file { 385 my $vol = Args::get_vol('vol'); 386 387 my $out_name = "$::host_dir" . "$::DATADIR/$Caseman::vol2sname{$vol}"; 388 my $i; 389 for ($i = 0; -e "${out_name}-${i}.srch"; $i++) { } 390 391 return "${out_name}-${i}.srch"; 392} 393 394# Pass the index 395# return the full path of the file returned 396sub get_srch_fname { 397 my $idx = shift; 398 my $vol = Args::get_vol('vol'); 399 return "$::host_dir" 400 . "$::DATADIR" 401 . "/$Caseman::vol2sname{$vol}-${idx}.srch"; 402} 403 404sub load { 405 Args::check_srchidx(); 406 407 Print::print_html_header(""); 408 409 if ($::LIVE == 1) { 410 print "Searches cannot be loaded during live analysis<br>\n"; 411 return 1; 412 } 413 414 my $srch_name = get_srch_fname($Args::args{'srchidx'}); 415 416 print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" 417 . "$Args::baseargs\" " 418 . "target=\"_parent\">New Search</a></b>\n<p>"; 419 420 print_srch_results($srch_name); 421 422 Print::print_html_footer(); 423 return 0; 424} 425 426# performs actual search, saves hits to file, and calls method to print 427sub run { 428 Args::check_str(); 429 430 Print::print_html_header(""); 431 432 my $vol = Args::get_vol('vol'); 433 my $ftype = $Caseman::vol2ftype{$vol}; 434 my $img = $Caseman::vol2path{$vol}; 435 my $offset = $Caseman::vol2start{$vol}; 436 my $imgtype = $Caseman::vol2itype{$vol}; 437 438 my $orig_str = Args::get_str(); 439 my $grep_str = $orig_str; # we will escape some values in the grep ver 440 441 # Check for a search string 442 if ($orig_str eq "") { 443 print "You must enter a string value to search<br>\n"; 444 print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" 445 . "$Args::baseargs\" target=\"_parent\">New Search</a></b>\n<p>"; 446 return 1; 447 } 448 449 my $log = ""; # Log entry string 450 451 my $ascii = 0; 452 my $unicode = 0; 453 454 if ((exists $Args::args{'ascii'}) && ($Args::args{'ascii'} == 1)) { 455 $ascii = 1; 456 $log .= "ASCII, "; 457 } 458 if ((exists $Args::args{'unicode'}) && ($Args::args{'unicode'} == 1)) { 459 $unicode = 1; 460 $log .= "Unicode, "; 461 } 462 463 if (($ascii == 0) && ($unicode == 0)) { 464 print "You must choose either ASCII or Unicode to search<br>\n"; 465 print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" 466 . "$Args::baseargs\" target=\"_parent\">New Search</a></b>\n<p>"; 467 return 1; 468 } 469 470 my $grep_flag = ""; # Flags to pass to grep 471 472 # Check if search is case insensitive 473 my $case = 0; 474 if ( (exists $Args::args{'srch_case'}) 475 && ($Args::args{'srch_case'} == $CASE_INSENS)) 476 { 477 $grep_flag = "-i"; 478 $case = 1; 479 $log .= "Case Insensitive "; 480 } 481 482 # Check if search is a regular expression 483 my $regexp = 0; 484 if ((exists $Args::args{'regexp'}) && ($Args::args{'regexp'} == $REG_EXP)) { 485 $grep_flag .= " -E"; 486 $regexp = 1; 487 $log .= "Regular Expression "; 488 } 489 490 # if not a reg-exp, we need to escape some special values that 491 # 'grep' will misinterpret 492 else { 493 $grep_str =~ s/\\/\\\\/g; # \ 494 $grep_str =~ s/\./\\\./g; # . 495 $grep_str =~ s/\[/\\\[/g; # [ 496 $grep_str =~ s/\^/\\\^/g; # ^ 497 $grep_str =~ s/\$/\\\$/g; # $ 498 $grep_str =~ s/\*/\\\*/g; # * 499 # We need to add ' to end begin and end of the search as well 500 if ($grep_str =~ /\'/) { 501 $grep_str =~ s/\'/\\\'/g; # ' 502 $grep_str = "'$grep_str'"; 503 } 504 $grep_str =~ s/^\-/\\\-/; # starting with - (mistakes for an arg) 505 } 506 507 Print::log_host_inv( 508 "$Caseman::vol2sname{$vol}: ${log}search for $grep_str"); 509 510 # Get the addressable unit of image 511 my $bs = Args::get_unitsize(); 512 513 # $norm_str is normalized to find the "hit" in the output 514 my $norm_str = $orig_str; 515 516 # make this lowercase if we are doing case insens 517 $norm_str =~ tr/[A-Z]/[a-z]/ if ($case == 1); 518 519 my $norm_str_len = length($norm_str); 520 521 # array to pass to printing method 522 my @results; 523 524 my $name_uni = ""; 525 my $name_asc = ""; 526 527 # round 0 is for ASCII and 1 is for Unicode 528 for (my $i = 0; $i < 2; $i++) { 529 530 next if (($i == 0) && ($ascii == 0)); 531 next if (($i == 1) && ($unicode == 0)); 532 533 @results = (); 534 535 local *OUT; 536 537 my $hit_cnt = 0; 538 $SIG{ALRM} = sub { 539 if (($hit_cnt++ % 5) == 0) { 540 print "+"; 541 } 542 else { 543 print "-"; 544 } 545 alarm(5); 546 }; 547 548 alarm(5); 549 550 if ($i == 0) { 551 print "<b>Searching for ASCII</b>: "; 552 } 553 else { 554 print "<b>Searching for Unicode</b>: "; 555 } 556 557 # if the string is less than 4 chars, then it will not be in the 558 # strings file so it will be searched for the slow way 559 if (length($orig_str) < 4) { 560 my $ltmp = length($orig_str); 561 562 if ($i == 0) { 563 if ( (($ftype eq "raw") || ($ftype eq "swap")) 564 && ($Caseman::vol2end{$vol} != 0)) 565 { 566 Exec::exec_pipe(*OUT, 567 "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " 568 . $Caseman::vol2start{$vol} . "-" 569 . $Caseman::vol2end{$vol} 570 . " | '$::TSKDIR/srch_strings' -a -t d -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" 571 ); 572 } 573 else { 574 Exec::exec_pipe(*OUT, 575"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" 576 ); 577 } 578 } 579 580 else { 581 if ( (($ftype eq "raw") || ($ftype eq "swap")) 582 && ($Caseman::vol2end{$vol} != 0)) 583 { 584 Exec::exec_pipe(*OUT, 585 "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " 586 . $Caseman::vol2start{$vol} . "-" 587 . $Caseman::vol2end{$vol} 588 . " | '$::TSKDIR/srch_strings' -a -t d -e l -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" 589 ); 590 } 591 else { 592 Exec::exec_pipe(*OUT, 593"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -e l -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" 594 ); 595 } 596 } 597 } 598 599 # Use the strings file if it exists 600 elsif (($i == 0) && (defined $Caseman::vol2str{$vol})) { 601 my $str_vol = $Caseman::vol2path{$Caseman::vol2str{$vol}}; 602 Exec::exec_pipe(*OUT, 603 "'$::GREP_EXE' $grep_flag '$grep_str' $str_vol"); 604 } 605 elsif (($i == 1) && (defined $Caseman::vol2uni{$vol})) { 606 my $str_vol = $Caseman::vol2path{$Caseman::vol2uni{$vol}}; 607 Exec::exec_pipe(*OUT, 608 "'$::GREP_EXE' $grep_flag '$grep_str' $str_vol"); 609 } 610 611 # Run strings on the image first and then grep that 612 else { 613 if ($i == 0) { 614 if ( (($ftype eq "raw") || ($ftype eq "swap")) 615 && ($Caseman::vol2end{$vol} != 0)) 616 { 617 Exec::exec_pipe(*OUT, 618 "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " 619 . $Caseman::vol2start{$vol} . "-" 620 . $Caseman::vol2end{$vol} 621 . " | '$::TSKDIR/srch_strings' -a -t d | '$::GREP_EXE' $grep_flag '$grep_str'" 622 ); 623 } 624 else { 625 Exec::exec_pipe(*OUT, 626"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d | '$::GREP_EXE' $grep_flag '$grep_str'" 627 ); 628 } 629 } 630 else { 631 if ( (($ftype eq "raw") || ($ftype eq "swap")) 632 && ($Caseman::vol2end{$vol} != 0)) 633 { 634 Exec::exec_pipe(*OUT, 635 "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " 636 . $Caseman::vol2start{$vol} . "-" 637 . $Caseman::vol2end{$vol} 638 . " | '$::TSKDIR/srch_strings' -a -t d -e l | '$::GREP_EXE' $grep_flag '$grep_str'" 639 ); 640 } 641 else { 642 Exec::exec_pipe(*OUT, 643"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -e l | '$::GREP_EXE' $grep_flag '$grep_str'" 644 ); 645 } 646 } 647 } 648 649 alarm(0); 650 $SIG{ALRM} = 'DEFAULT'; 651 652 # Cycle through the results and put them in an array 653 while ($_ = Exec::read_pipe_line(*OUT)) { 654 655 # Parse out the byte offset and hit string 656 if (/^\s*(\d+)\s+(.+)$/) { 657 my $off = $1; 658 my $hit_str_orig = $2; 659 my $idx = 0; 660 661 # Make a copy that we can modify & play with 662 my $hit_str = $hit_str_orig; 663 $hit_str =~ tr/[A-Z]/[a-z]/ if ($case == 1); 664 665 # How long was the string that we hit? 666 my $hit_str_len = length($hit_str); 667 668 # I'm not sure how to find a grep re in the hit yet, so 669 # for now we do not get the exact offset 670 if ($regexp) { 671 my $b = int($off / $bs); 672 my $o = int($off % $bs); 673 674 # $hit =~ s/\n//g; 675 push @results, "${b}|${o}|"; 676 next; 677 } 678 679 # There could be more than one keyword in the string 680 # so cycle through all of them 681 my $psize = scalar(@results); 682 while (($idx = index($hit_str, $norm_str, $idx)) > -1) { 683 684 # The summary of the hit starts 5 chars before it 685 my $sum_min = $idx - 5; 686 $sum_min = 0 if ($sum_min < 0); 687 688 # Goto 5 after, if there is still space 689 my $sum_max = $idx + $norm_str_len + 5; 690 $sum_max = $hit_str_len if ($sum_max > $hit_str_len); 691 692 my $sum_hit = 693 substr($hit_str_orig, $sum_min, $sum_max - $sum_min); 694 695 # remove new lines 696 $sum_hit =~ s/\n/ /g; 697 698 # The actual offset for Unicode is 2 bytes per char 699 my $tmpidx = $idx; 700 $tmpidx *= 2 701 if ($i == 1); 702 703 my $b = int(($off + $tmpidx) / $bs); 704 my $o = int(($off + $tmpidx) % $bs); 705 706 push @results, "${b}|${o}|$sum_hit"; 707 708 # advance index to find next hit 709 $idx++; 710 } 711 712 # If we did not find a term, then just print what 713 # was found-this occurs bc index does not find it 714 # sometimes. 715 if ($psize == scalar(@results)) { 716 717 my $b = int($off / $bs); 718 my $o = int($off % $bs); 719 720 # $hit =~ s/\n//g; 721 push @results, "${b}|${o}|"; 722 next; 723 } 724 } 725 726 # A negative offset is common on FreeBSD with large images 727 elsif (/^\s*(\-\d+):?\s*(.+)$/) { 728 print "ERROR: Negative byte offset ($1) Your version of " 729 . "strings likely does not support large files: $2<br>\n"; 730 } 731 else { 732 print "Error parsing grep result: $_<br>\n"; 733 } 734 } 735 close(OUT); 736 737 print " <b>Done</b><br>"; 738 my $cnt = scalar(@results); 739 740 my $srch_name = ""; 741 if ($::LIVE == 0) { 742 print "<b>Saving</b>: "; 743 744 # Find a file to save the results to 745 $srch_name = find_srch_file(); 746 unless (open(IDX, ">$srch_name")) { 747 print "Error opening $srch_name\n"; 748 return (1); 749 } 750 751 # Print the header 752 if ($i == 0) { 753 print IDX "$cnt|${grep_flag}|${orig_str}|ascii\n"; 754 $name_asc = $srch_name; 755 } 756 else { 757 print IDX "$cnt|${grep_flag}|${orig_str}|unicode\n"; 758 $name_uni = $srch_name; 759 } 760 761 for (my $a = 0; $a < $cnt; $a++) { 762 print IDX "$results[$a]\n"; 763 } 764 close(IDX); 765 print " <b>Done</b><br>\n"; 766 } 767 if ($i == 0) { 768 print "$cnt hits"; 769 print "- <a href=\"#ascii\">link to results</a>" if ($cnt > 0); 770 print "<br>\n"; 771 } 772 else { 773 print "$cnt hits"; 774 print "- <a href=\"#unicode\">link to results</a>" if ($cnt > 0); 775 print "<br>\n"; 776 } 777 print "<hr>\n"; 778 } 779 780 print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" 781 . "$Args::baseargs\" " 782 . "target=\"_parent\">New Search</a></b>\n<p>"; 783 784 if ($::LIVE == 0) { 785 if ($ascii == 1) { 786 print_srch_results($name_asc); 787 } 788 789 if ($unicode == 1) { 790 print_srch_results($name_uni); 791 } 792 } 793 794 Print::print_html_footer(); 795 return 0; 796} 797 798# Args are search string, grep flags, and array of hits 799sub print_srch_results { 800 801 if (scalar(@_) != 1) { 802 print "Missing Args for print_srch_results()\n"; 803 return 1; 804 } 805 806 my $srch_name = shift; 807 my $vol = Args::get_vol('vol'); 808 my $ftype = $Caseman::vol2ftype{$vol}; 809 810 my $addr_str = $Fs::addr_unit{$ftype}; 811 812 unless (open(SRCH, "$srch_name")) { 813 print "Error opening search file: $srch_name\n"; 814 return 1; 815 } 816 817 my @results; 818 my $grep_str = ""; 819 my $grep_flag = ""; 820 my $cnt = 0; 821 my $type = 0; # ASCII iis 0 and Unicode is 1 822 823 my $prev = -1; 824 825 while (<SRCH>) { 826 827 # The first line is a header 828 if ($. == 1) { 829 if (/^(\d+)\|(.*?)?\|(.*)$/) { 830 $cnt = $1; 831 $grep_flag = $2; 832 $grep_str = $3; 833 $type = 0; 834 } 835 else { 836 print "Error pasing header of search file: $srch_name\n"; 837 close(SRCH); 838 return 1; 839 } 840 841 if ($grep_str =~ /^(.*?)\|unicode$/) { 842 $grep_str = $1; 843 $type = 1; 844 } 845 elsif ($grep_str =~ /^(.*?)\|ascii$/) { 846 $grep_str = $1; 847 } 848 849 my $grep_str_html = Print::html_encode($grep_str); 850 print "<hr>\n"; 851 if ($type == 0) { 852 print "<a name=\"ascii\">\n"; 853 } 854 else { 855 print "<a name=\"unicode\">\n"; 856 } 857 858 if ($cnt == 0) { 859 print "<b><tt>$grep_str_html</tt> was not found</b><br>\n"; 860 } 861 elsif ($cnt == 1) { 862 print 863"<b>1 occurrence of <tt>$grep_str_html</tt> was found</b><br>\n"; 864 } 865 else { 866 print 867"<b>$cnt occurrences of <tt>$grep_str_html</tt> were found</b><br>\n"; 868 } 869 870 print "Search Options:<br>\n"; 871 if ($type == 0) { 872 print " ASCII<br>\n"; 873 } 874 else { 875 print " Unicode<br>\n"; 876 } 877 878 if ($grep_flag =~ /\-i/) { 879 print " Case Insensitive<br>\n"; 880 } 881 else { 882 print " Case Sensitive<br>\n"; 883 } 884 if ($grep_flag =~ /\-E/) { 885 print " Regular Expression<br>\n"; 886 } 887 888 print "<hr>\n"; 889 890 if ($cnt > 1000) { 891 print "There were more than <U>1000</U> hits.<br>\n"; 892 print "Please revise the search to a managable amount.\n"; 893 print 894 "<p>The $cnt hits can be found in: <tt>$srch_name</tt><br>\n"; 895 close(SRCH); 896 return 0; 897 } 898 899 next; 900 } 901 902 unless (/^(\d+)\|(\d+)\|(.*)?$/) { 903 print "Error parsing results: $_\n"; 904 close(SRCH); 905 return 1; 906 } 907 908 my $blk = $1; 909 my $off = $2; 910 my $str = $3; 911 912 if ($blk != $prev) { 913 my $url = 914"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&$Args::baseargs&block=$blk"; 915 916 print "<br>\n$addr_str $blk (<a href=\"$url&sort=$Data::SORT_HEX\" " 917 . "target=content>Hex</a> - " 918 . "<a href=\"$url&sort=$Data::SORT_ASC\" target=content>" 919 . "Ascii</a>"; 920 921 print 922" - <a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" 923 . "mnt=$Args::enc_args{'mnt'}&vol=$Caseman::mod2vol{$vol}&" 924 . "btype=$Data::ADDR_BLKLS&block=$blk\" target=content>Original</a>" 925 if ( ($ftype eq 'blkls') 926 && (exists $Caseman::mod2vol{$vol})); 927 928 print ")<br>"; 929 $prev = $blk; 930 } 931 932 my $occ = $. - 1; 933 if ($str ne "") { 934 $str = Print::html_encode($str); 935 print "$occ: $off (<tt>$str</tt>)<br>\n"; 936 } 937 else { 938 print "$occ: $off<br>\n"; 939 } 940 } 941 942 close(SRCH); 943 return 0; 944} 945 946# Blank Page 947sub blank { 948 Print::print_html_header(""); 949 print "<!-- This Page Intentionally Left Blank -->\n"; 950 Print::print_html_footer(); 951 return 0; 952} 953 9541; 955