diff --git a/NEWS.txt b/CHANGES.txt similarity index 100% rename from NEWS.txt rename to CHANGES.txt diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..5b6e7c66c276e7610d4a73c70ec1a1f7c1003259 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000000000000000000000000000000000000..ef406a4845b1d9bee74f914fdbd193e72b181a75 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,39 @@ + Autopsy Forensic Browser + http://www.sleuthkit.org/autopsy + + Brian Carrier [carrier@sleuthkit.org] + Last Update: March 2010 + + +Installation +----------------------------------------------------------------------------- +1. Install The Sleuth Kit (see README for download locations). This + includes doing a 'make install' so that the executables and files + are installed in a single directory. Note that this does not + currently work natively on Windows. It will work using Cygwin, but + you must install and build The Sleuth Kit in Cygwin and not use the + Win32 executables that are available on the sleuthkit.org website. + +2. Untar the Autopsy file. + +3. Run 'make'. It will try to locate the grep and strings utilities. + If any are not found, it will prompt you for the location. It will + also search for the TSK installation. + +4. The install script will ask if you have the NIST National Software + Reference Library (NSRL). If you do, you will need to enter the + path of it. The NSRL is available from www.nsrl.nist.gov. + +5. You will be prompted for the Evidence Locker location. This is the + base directory where all cases will be stored. You must create this + directory on your own. + + +Live Analysis +------------------------------------------------------------------------------ +Type 'make live' or run the 'make-live-cd' script to build the 'live-cd' +directory. The 'live-cd' directory can be burned to a CD. + +------------------------------------------------------------------------------ +Brian Carrier [carrier@sleuthkit.org] + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..3a6334c19601b146f1ac71a11128e37b11d821c2 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +all: + @sh ./configure + +conf.pl: + @sh ./configure + +live: conf.pl + ./make-live-cd + +indent: + cd lib; perltidy -b *.pm *.pl + cd base; perltidy -b autopsy.base + +clean: + rm -f ./autopsy + rm -f ./make-live-cd + rm -f ./conf.pl + rm -f ./config.tmp + rm -f ./config2.tmp + rm -rf ./live-cd/ + rm -f ./lib/*.bak + rm -f ./base/*.bak + find . -name ".DS_Store" | xargs rm -f + find . -type f -perm +g+x,o+x,u+x | xargs chmod -x + grep "curtskver=" ./configure + grep "VER" ./lib/define.pl + find . -name ".*" | grep -v perltidy + +release: + find . -name "CVS" | xargs rm -rf diff --git a/README-LIVE.txt b/README-LIVE.txt new file mode 100644 index 0000000000000000000000000000000000000000..df1aaabdc6d0c10029458d27309cb4b345f0fb76 --- /dev/null +++ b/README-LIVE.txt @@ -0,0 +1,119 @@ + Autopsy Forensic Browser + http://www.sleuthkit.org/autopsy + + Live Analysis Mode + + Last Updated: January 2005 + + +What is Live Analysis? +-------------------------------------------------------------------- +Live analysis is, in my mind, an investigation that occurs using +the software resources of the suspect system. An example scenario +of this is when a suspect system is found running, a CD is placed +into it, and commands are run. If the suspect system is powered +down and booted from a bootable Linux CD (or similar), then the +investigation is a dead analysis. + +This is most commonly done when investigating a server or other +computer that is suspected of being compromised, but verification +is needed before it can be powered down. Using The Sleuth Kit and +Autopsy will prevent the access times on individual files from being +updated (although the raw device's A-time will be) and can bypass +most rootkits that hide files and directories. + + +What are the Issues with Live Analysis? +-------------------------------------------------------------------- +Live analysis is not ideal because you are relying on the suspect +system, which can lie, cheat, and steal. In addition to the potential +of getting false information from the operating system you will +also overwrite memory and maybe swap space during the investigation. + +If you are interested in examining the memory of the system, you +should probably acquire that before you begin a live analysis. + +An issue with doing live analysis with Autopsy is that it requires +Perl, which is a large program and will likely need to depend on +libraries and other files on the suspect system. + + +How do I make a CD with Autopsy on it? +-------------------------------------------------------------------- + +You will want to have a trusted CD for a live analysis, and autopsy +makes that fairly easy. Compile autopsy as you would for a normal +dead analysis installation. Then execute 'make live' in Autopsy. +This script will make a 'live-cd' sub-directory in the autopsy directory, +which contains a copy of autopsy and copies of TSK executables, grep, +strings, perl etc: + + # make live + Making base directory (./live-cd/) + Copying executables + Copying autopsy files + Creating configuration file using existing settings + +Try the 'make static' with TSK to see if you can make static +executables for your platform. + +The 'live-cd' directory has a 'bin' directory where additional +executables can be copied to and then the whole directory can be +burned to a CD. + + +How Do I Use the CD? +-------------------------------------------------------------------- + +After the CD has been created and there is a system suspected of +being compromised, then it is time to take advantage of the new +features. There are two scenarios for live analysis. The first +scenario uses a network share from a trusted system that you can +write to. In this case, autopsy is run as normal and you specify +the evidence locker directory as the mounted disk. The evidence +locker is specified with '-d': + + # ./autopsy -d /mnt/ev_lock 10.1.32.123 + +The above would start autopsy, use '/mnt/ev_lock/' as the evidence +locker and would allow connections from 10.1.32.123 (where the +investigator would connect from using an HTML browser). Remember that +we do not want to write to the suspect system, so we should only use +a network share and not a local directory in this scenario. + +The second scenario does not use an evidence locker and does not +intentionally write any data to disk. This scenario does not need +the network share and each of the devices (or partitions) that will +be analyzed are specified on the command line using the '-i' flags. +The '-i' flag requires three arguments: the device, the file system +type, and the mounting point. For example, to examine the '/dev/hda5' +and '/dev/hda8' partitions on a Linux system, the following could +be used: + + # ./autopsy -i /dev/hda5 linux-ext3 / -i /dev/hda8 linux-ext3 /usr/ \ + 10.1.32.123 + +The file system type must be one of the types that are supported +by TSK. The remote IP address must also be given, otherwise you +will have to use a browser on the suspect system and that will write +data to the disk. + +When you use the '-i' flag, then autopsy will start in the 'Host +Manager' view where you can select the image that you want to +analyze. You will skip the case and host configuration. The default +case name will be 'live', the default host name is 'local', and the +default investigator name is 'unknown'. + + +Additional Information +-------------------------------------------------------------------- +I wrote a more detailed explanation of the live analysis mode of +Autopsy version 2.00 in the 13th issue of The Sleuth Kit Informer. +Some of this document is taken from the Informer issue. + + http://www.sleuthkit.org/informer/sleuthkit-informer-13.html + + +-------------------------------------------------------------------- +Copyright (c) 2004 by Brian Carrier. All Rights Reserved +Brian Carrier [carrier <at> sleuthkit <dot> org] diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000000000000000000000000000000000000..d8a262840f382c4d9341b4bef6b27aa8dc45fe3d --- /dev/null +++ b/TODO.txt @@ -0,0 +1,58 @@ +Remove HFS check in autopsy.base when HFS support is standard. + +Look into adding the unallocated partiion space when an image is added. +Look into integrating clamshell or other AV + +There is an error when viewing the event sequencer notes about +initialized in "ne". + +test date search + +Add SHA-1 to more places (images, lookups etc.) + +Get "Can't ignore signal CHLD, forcing to default." message with +file type sorting (BUG: 919829). + +Figure out details around having Perl on the live analysis CD. +(BUG: 919831) + + +-------------------- FILE MODE ---------------------------- +- Make a file system depend seperator for / or \ + +- check for tar during install + - List contents like HTML does now + +--------------------- SEARCH ----------------------------- +- new mode where searching is done on output of 'icat' of allocated + inodes + +- Bug: if the string that matches a keyword starts with spaces, then + the 'index' function returns the idx to the start of the spaces and not + the substring. (BUG 842858) + +--------------------- TIMELINE ----------------------------- + + +--------------------- LOGGING ----------------------------- +- New Report Creation: + +- Add pulldown in notes for common things: + - New MD5 / SHA-1 + - Part of rootkit + - Suspected child porn + - known child porn + +--------------------- SORTER ----------------------------- +- Should sorter be at the host level instead of image, with output +files appended to each other? + +- Allow one to browse output files + +--------------------- GENERAL ----------------------------- +- Add foremost +- link in meta data to list just unallocated / used +- Make data bases updatable in the host details view +- Option to mount images in loopback when it is a Linux system +- read config files in autopsy itself and not everytime ... +- Make a way to kill intensive processes (searching, fls) diff --git a/base/.perltidyrc b/base/.perltidyrc new file mode 100755 index 0000000000000000000000000000000000000000..f1f2e71a01bcfcdb8bd171c25e4d7a1e1e797c93 --- /dev/null +++ b/base/.perltidyrc @@ -0,0 +1,5 @@ +-i=4 # indent of 4 +-pt=2 # paren tightness +-sbt=2 # square paren tightness +-bt=2 # curly paren tightness +-nsfs # no space after semi in for loop diff --git a/base/autopsy.base b/base/autopsy.base new file mode 100644 index 0000000000000000000000000000000000000000..3b3bbdc7f2a8bd2ae3882e51d4b8a74116278a29 --- /dev/null +++ b/base/autopsy.base @@ -0,0 +1,879 @@ +# +# autopsy gui server +# Autopsy Forensic Browser +# +# +# This file requires The Sleuth Kit +# www.sleuthkit.org +# +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# refer to Security Considerations in README for a description of the +# cookie authentication +# + +require 5.008; + +use strict; +use Socket; + +use Main; +use Print; +require Fs; +require Caseman; + +require 'conf.pl'; +require 'lib/define.pl'; + +# Import variables from conf.pl +use vars '$LOCKDIR', '$INSTALLDIR', '$PICTDIR'; +use vars '$SANITIZE_TAG', '$SANITIZE_PICT'; +use vars '$USE_STIMEOUT', '$STIMEOUT', '$CTIMEOUT'; +use vars '$SAVE_COOKIE', '$GREP_EXE', '$FILE_EXE'; +use vars '$NSRLDB'; + +# Default port +my $port = 9999; + +# Default 'remote' host +my $rema = 'localhost'; + +$| = 1; + +$::LIVE = 0; +$::USE_NOTES = 1; +$::USE_LOG = 1; + +sub usage { + print +"\n\nusage: $0 [-c] [-C] [-d evid_locker] [-i device filesystem mnt] [-p port] [remoteaddr]\n"; + print " -c: force a cookie in the URL\n"; + print " -C: force NO cookie in the URL\n"; + print " -d dir: specify the evidence locker directory\n"; + print " -i device filesystem mnt: Specify info for live analysis\n"; + print " -p port: specify the server port (default: $port)\n"; + print " remoteaddr: specify the host with the browser (default: $rema)\n"; + exit 1; +} + +my $cook_force = 0; + +my $vol_cnt = 0; + +# Were options given? +while ((scalar(@ARGV) > 0) && ($ARGV[0] =~ /^-/)) { + my $f = shift; + + # Evidence Locker + if ($f eq '-d') { + if (scalar(@ARGV) == 0) { + print "Missing Directory\n"; + usage(); + } + + my $d = shift; + + # We need to do this for the tainting + # We don't need to check for special characters in this case because + # all commands will be run with the same permissions as the + # original user. We will check for the obvious ';' though + if ($d =~ /;/) { + print "Illegal argument\n"; + exit(1); + } + + # If the path is relative, autopsyfunc will get screwed up when + # this is run from a directory other than where autopsyfunc is + # so force full paths + elsif ($d !~ /^\//) { + print "The evidence locker must be full path (i.e. begin with /)\n"; + exit(1); + } + elsif ($d =~ /(.*)/) { + $LOCKDIR = $1; + } + } + + # Force no cookie + elsif ($f eq '-C') { + $::USE_COOKIE = 0; + $cook_force = 1; + } + + # force a cookie + elsif ($f eq '-c') { + $::USE_COOKIE = 1; + $cook_force = 1; + } + + elsif ($f eq '-i') { + $::LIVE = 1; + $::USE_LOG = 0; + $::USE_NOTES = 0; + $::SAVE_COOKIE = 0; + + if (scalar(@ARGV) < 3) { + print "Missing device, file system, and mount point arguments\n"; + usage(); + } + + my $vol = "vol" . $vol_cnt; + $vol_cnt++; + + my $dev = shift; + if ($dev =~ /($::REG_IMG_PATH)/) { + $dev = $1; + } + else { + print "invalid device: $dev\n"; + usage(); + } + + unless ((-e "$dev") || (-l "$dev")) { + print "Device ($dev) not found\n"; + usage(); + } + + my $fs = shift; + if ($fs =~ /($::REG_FTYPE)/) { + $fs = $1; + } + else { + print "invalid file system: $fs\n"; + usage(); + } + unless ((exists $Fs::root_meta{$fs}) + && (defined $Fs::root_meta{$fs})) + { + print "File system not supported: $fs\n"; + usage(); + } + $Caseman::vol2ftype{$vol} = "$fs"; + + my $mnt = shift; + if ($mnt =~ /($::REG_MNT)/) { + $mnt = $1; + } + else { + print "invalid mount point: $mnt\n"; + usage(); + } + $Caseman::vol2mnt{$vol} = "$mnt"; + $Caseman::vol2cat{$vol} = "part"; + $Caseman::vol2itype{$vol} = "raw"; + $Caseman::vol2start{$vol} = 0; + $Caseman::vol2end{$vol} = 0; + + # This makes me nervous ... + $Caseman::vol2par{$vol} = $vol; + $Caseman::vol2path{$vol} = "$dev"; + $Caseman::vol2sname{$vol} = "$dev"; + } + + # Specify a different port + elsif ($f eq '-p') { + if (scalar(@ARGV) == 0) { + print "Missing port argument\n"; + usage(); + } + + my $p = shift; + if ($p =~ /(\d+)/) { + $p = $1; + } + else { + print "invalid port: $p\n"; + usage(); + } + if (($p < 1) || ($p > 65535)) { + print "invalid port: $port\n"; + usage(); + } + $port = $p; + } + + else { + print "Invalid flag: $f\n"; + usage(); + } +} + +# remote address +if (scalar(@ARGV) > 0) { + $rema = shift; +} + +# Get remote address +my @acl_addr; # Array of host addresses +my $hn; # Host name +my $tmp; +if ($rema =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/) { + $acl_addr[0] = pack('C4', ($1, $2, $3, $4)); + $hn = $rema; +} +else { + ($hn, $tmp, $tmp, $tmp, @acl_addr) = gethostbyname($rema); + unless (defined $tmp) { + print "Host not found: $rema\n"; + usage(); + } +} + +# Determine the address that will be used to access this server +my $lclhost; +my @ta = unpack('C4', $acl_addr[0]); + +my $bindaddr; + +# If we are being accessed by localhost, we need that and not the hostname +if ( ($ta[0] == 127) + && ($ta[1] == 0) + && ($ta[2] == 0) + && ($ta[3] == 1)) +{ + $lclhost = "localhost"; + $bindaddr = $acl_addr[0]; + + # Force no cookie to be used unless we already set this value + # with arguments + $::USE_COOKIE = 0 unless ($cook_force == 1); +} +else { + $lclhost = `/bin/hostname`; + chop $lclhost; + + $bindaddr = INADDR_ANY; + + # Force a cookie to be used unless we already set this value + # with arguments + $::USE_COOKIE = 1 unless ($cook_force == 1); +} + +# Verify the variables defined in the configuration files +check_vars(); + +# Remove the final '/' from TSKDIR if it exists +$::TSKDIR = $1 + if ($::TSKDIR =~ /(.*?)\/$/); + +# +# Verify that all of the required executables exist +# +check_tools(); + + + +# Currently, HFS is in beta and not enabled by default. +# Autopsy has been configured for it though, so disable it if +# the user has not compiled support into TSK. remove this when +# HFS support is standard. +# This redirects stderr to stdout so we can easily capture it +my $out = `\'$::TSKDIR/fls\' -f list 2>&1`; +unless ($out =~ /hfs/) { + for (my $i = 0; $i < @Fs::types; $i++) { + if ($Fs::types[$i] eq "hfs") { + $Fs::types[$i] = ""; + last; + } + } +} + + + +# remove environment stuff that we don't need and that could be insecure +# We allow basic bin directories for CYGWIN though, since they are +# required for the CYGWIN dlls +my $UNAME = ""; +if (-e "/bin/uname") { + $UNAME = "/bin/uname"; +} +elsif (-e "/usr/bin/uname") { + $UNAME = "/usr/bin/uname"; +} + +my $ispathclear = 1; +if (($UNAME ne "") && (`$UNAME` =~ /^CYGWIN/)) { + $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; + $ispathclear = 0; +} +else { + $ENV{PATH} = ''; +} +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +my $date = localtime; + +if ($::LIVE == 0) { + + # Remove the final '/' if it exists + $LOCKDIR = $1 + if ($LOCKDIR =~ /(.*?)\/$/); +} + +# Setup socket +my $proto = getprotobyname('tcp'); +socket(Server, PF_INET, SOCK_STREAM, $proto) + or die "Error creating network socket: $!"; + +setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, 1) + or die "Error setting network socket options (reuse): $!"; + +setsockopt(Server, SOL_SOCKET, SO_KEEPALIVE, 1) + or die "Error setting network socket options (keep alive): $!"; + +bind(Server, sockaddr_in($port, $bindaddr)) + or die "Error binding to port $port (is Autopsy already running?): $!"; + +listen(Server, SOMAXCONN) + or die "Error listening to socket for connections: $!"; + +my $magic; # magic authentication cookie +my $cook_file; +my $cookie_url = ""; + +if ($::USE_COOKIE == 1) { + + # Try for a real random device, or use rand if all else fails + if (-e "/dev/urandom") { + my $r; + open RAND, "</dev/urandom" or die "can not open /dev/urandom"; + read RAND, $r, 4; + $magic = unpack "I", $r; + read RAND, $r, 4; + $magic .= unpack "I", $r; + close RAND; + } + else { + $magic = int(rand 0xffffffff) . int(rand 0xffffffff); + } + + $cookie_url = "$magic/"; + + # Save to file in case the stdout gets overwritten + if ($SAVE_COOKIE == 1) { + $cook_file = "$LOCKDIR/.$port.cookie"; + if (open COOK, ">$cook_file") { + chmod 0600, "$cook_file"; + print COOK "$magic\n"; + close COOK; + } + else { + print "WARNING: Cannot open file to save cookie in ($cook_file)"; + } + } +} + +print <<EOF; + +============================================================================ + + Autopsy Forensic Browser + http://www.sleuthkit.org/autopsy/ + ver $::VER + +============================================================================ +EOF + +if ($::LIVE == 0) { + print "Evidence Locker: $LOCKDIR\n"; +} +else { + print "Live Analysis Mode\n"; +} +if ($ispathclear == 0) { + print +"\nCYGWIN Mode (Internal path contains /bin, /usr/bin, and /usr/local/bin)\n\n"; +} + +print <<EOF2; +Start Time: $date +Remote Host: $rema +Local Port: $port + +Open an HTML browser on the remote host and paste this URL in it: + + http://$lclhost:${port}/${cookie_url}$::PROGNAME + +Keep this process running and use <ctrl-c> to exit +EOF2 + +Print::log_session_info("Starting session on port $port and $hn\n"); + +# Set the server alarm +$SIG{ALRM} = \&SIG_ALARM_SERVER; +$SIG{INT} = \&SIG_CLOSE; + +# setting this to ignore will automatically wait for children +$SIG{CHLD} = 'IGNORE'; + +# Wait for Connections +while (1) { + + alarm($STIMEOUT) if ($USE_STIMEOUT == 1); + + my $raddr = accept(CLIENT, Server); + next unless ($raddr); + my ($rport, $riaddr) = sockaddr_in($raddr); + + die "Error creating child" unless (defined(my $pid = fork())); + + if (0 == $pid) { + open(STDOUT, ">&CLIENT") or die "Can't dup client to stdout"; + + # open(STDERR, ">&CLIENT") or die "Can't dup client to stdout"; + open(STDIN, "<&CLIENT") or die "Can't dup client to stdin"; + $| = 1; + + my @rip = unpack('C4', $riaddr); + + # Check ACL + foreach $tmp (@acl_addr) { + if ($tmp eq $riaddr) { + spawn_cli($riaddr); + close CLIENT; + exit 0; + } + } + + forbid("$rip[0].$rip[1].$rip[2].$rip[3]"); + Print::log_session_info("ERROR: Unauthorized Connection from: " + . "$rip[0].$rip[1].$rip[2].$rip[3]\n"); + + close CLIENT; + exit 1; + } + else { + close CLIENT; + } +} + +# Error messages +sub forbid { + my $ip = shift; + $ip = "" unless defined ($ip); + + print "HTTP/1.0 403 Forbidden$::HTTP_NL" + . "Content-type: text/html$::HTTP_NL$::HTTP_NL" + . "<html><center>\n" + . "<h2>Access Denied</h2>\n" + . "<h3>Your connection from: $ip has been logged</h3>\n" + . "</center></html>$::HTTP_NL$::HTTP_NL$::HTTP_NL"; + + return; +} + +sub bad_req { + print "HTTP/1.0 404 Bad Request$::HTTP_NL" + . "Content-type: text/html$::HTTP_NL$::HTTP_NL" + . "<html><body><center>\n" + . "<h2>Invalid URL<br><tt>" + . shift() + . "</tt></h2>\n" + . "</center></body></html>" + . "$::HTTP_NL$::HTTP_NL$::HTTP_NL"; + + return; +} + +# Alarm Functions +sub SIG_ALARM_CLIENT { + Print::log_session_info("Connection timed out\n"); + close CLIENT; + exit 1; +} + +sub SIG_ALARM_SERVER { + print "Server Timeout ($STIMEOUT seconds), Exiting\n"; + Print::log_session_info("Server Timeout ($STIMEOUT seconds), Exiting\n"); + exit 0; +} + +# Close the system down when Control-C is given +sub SIG_CLOSE { + + # delete the cookie file + if (($::USE_COOKIE == 1) && ($SAVE_COOKIE == 1)) { + unlink "$cook_file"; + } + + print "End Time: " . localtime() . "\n"; + Print::log_session_info("Ending session on port $port and $hn\n"); + exit 0; +} + +# Pass the remote IP address as the argument for logging +sub spawn_cli { + + # Set timeout for 10 seconds if we dont get any input + alarm($CTIMEOUT); + $SIG{ALRM} = \&SIG_ALARM_CLIENT; + + while (<STDIN>) { + + last if (/^\s+$/); + + if (/^GET \/+(\S*)\s?HTTP/) { + my $url = $1; + my $script; + my $args; + + if (/\x0d\x0a$/) { + $::HTTP_NL = "\x0d\x0a"; + } + else { + $::HTTP_NL = "\x0a"; + } + + # Magic Cookie + # If we are using cookies, then the url should be: + # cookie/autopsy?var=val ... + if ($::USE_COOKIE == 1) { + + if ( ($url =~ /^(\d+)\/+([\w\.\/]+)(?:\?(.*))?$/) + && ($1 == $magic)) + { + $script = $2; + $args = $3; + } + else { + my @rip = unpack('C4', shift()); + Print::log_session_info("ERROR: Incorrect Cookie from: " + . "$rip[0].$rip[1].$rip[2].$rip[3]\n"); + forbid("$rip[0].$rip[1].$rip[2].$rip[3]"); + return 1; + } + } + + # if we aren't using cookies, then it should be: + # autopsy?var=val ... + else { + if ($url =~ /^\/?([\w\.\/]+)(?:\?(.*))?$/) { + $script = $1; + $args = $2; + } + else { + bad_req($url); + return 1; + } + } + + if ($script eq $::PROGNAME) { + $args = "" unless (defined $args); + + # Turn timer off + alarm(0); + + my $has_ref = 0; + while (<STDIN>) { + last if (/^\s+$/); + if (/^Referer: /) { + $has_ref = 1; + last; + } + } + + if (($has_ref == 0) && ($args ne "")) { + Print::log_session_info("ERROR: Missing referer value"); + forbid(); + return 1; + } + + # Print status + print "HTTP/1.0 200 OK$::HTTP_NL"; + ::main($args); + } + elsif ($script eq "global.css") { + show_file($script); + } + + # Display the sanitized picture or reference error + elsif ($script eq $::SANITIZE_TAG) { + Appview::sanitize_pict($args); + return 1; + } + + # Display a picture or help file + elsif (($script =~ /^(pict\/[\w\.\/]+)/) + || ($script =~ /^(help\/[\w\.\/]+)/)) + { + show_file($1); + } + elsif ($script eq 'about') { + about(); + } + + # I'm not sure why this is needed, but there are reqs for it + elsif ($script eq 'favicon.ico') { + show_file("pict/favicon.ico"); + } + else { + bad_req($url); + Print::log_session_info("Unknown function: $script\n"); + return 1; + } + return 0; + } + } # end of while (<>) + +} # end of spawn_cli + +# Print the contents of a local picture or help file +sub show_file { + my $file = "$INSTALLDIR/" . shift; + + if (-e "$file") { + print "HTTP/1.0 200 OK$::HTTP_NL"; + + open FILE, "<$file" + or die "can not open $file"; + + if ($file =~ /\.css$/i) { + print "Content-type: text/css$::HTTP_NL$::HTTP_NL"; + } + elsif ($file =~ /\.jpg$/i) { + print "Content-type: image/jpeg$::HTTP_NL$::HTTP_NL"; + } + elsif ($file =~ /\.gif$/i) { + print "Content-type: image/gif$::HTTP_NL$::HTTP_NL"; + } + elsif ($file =~ /\.ico$/i) { + print "Content-type: image/ico$::HTTP_NL$::HTTP_NL"; + } + elsif ($file =~ /\.html$/i) { + print "Content-type: text/html$::HTTP_NL$::HTTP_NL"; + } + else { + print "HTTP/1.0 404 Bad Request$::HTTP_NL" + . "Content-type: text/html$::HTTP_NL$::HTTP_NL" + . "<html>\n" + . "<head><title>Error</title></head>\n" + . "<h2><center>Unknown Extension</h2>\n" + . "</center></html>$::HTTP_NL$::HTTP_NL$::HTTP_NL"; + exit(1); + } + + while (<FILE>) { + print "$_"; + } + close(FILE); + + print "$::HTTP_NL$::HTTP_NL"; + } + else { + print "HTTP/1.0 404 Bad Request$::HTTP_NL" + . "Content-type: text/html$::HTTP_NL$::HTTP_NL" + . "<html>\n" + . "<head><title>Error</title></head>\n" + . "<h2><center>File Not Found</h2>" + . "</center></html>$::HTTP_NL$::HTTP_NL$::HTTP_NL"; + exit(1); + } + + return; +} + +sub about { + + print "HTTP/1.0 200 OK$::HTTP_NL" + . "Content-type: text/html$::HTTP_NL$::HTTP_NL"; + + my $tskver = ::get_tskver(); + + print <<EOF; + +<html> +<head><title>About Autopsy</title></head> + +<body BGCOLOR=#CCCC99> + +<center><h2>About Autopsy</h2> + <br> + <img src=\"pict/logo.jpg\" alt=\"Logo\"> + <br><br> + <b>Version</b>: $::VER + <br> + <tt><a href="http://www.sleuthkit.org/autopsy/">http://www.sleuthkit.org/autopsy/</a></tt> + <br> + <tt><a href="http://www.sleuthkit.org/informer/">http://www.sleuthkit.org/informer/</a></tt> +</center> + + +<h3>Credits</h3> +<UL> + <LI>Code Development: Brian Carrier (carrier at sleuthkit dot org) + <LI>Interface Assistance: Samir Kapuria + <LI>Mascot: Hash the Hound +</UL> + +<h3>Configuration</h3> +<b>The Sleuth Kit</b>:<br> + URL: <a href="http://www.sleuthkit.org/sleuthkit/"> + <tt>http://www.sleuthkit.org/sleuthkit/</tt></a><br> + Installation Location: <tt>$::TSKDIR</tt><br> + Version: $tskver<br> +<b>Evidence Locker</b>: <tt>$LOCKDIR</tt><br> +<b>grep</b>: <tt>$GREP_EXE</tt><br> +<b>file</b>: <tt>$FILE_EXE</tt><br> +<b><a href="http://www.nsrl.nist.gov/">NIST NSRL</a></b>: <tt>$NSRLDB</tt><br> + +</body></html> + +EOF + return 0; +} + +### Check that the required tools are there +sub check_tools { + + # Sleuth Kit execs + unless (-x $::TSKDIR . "/icat") { + print "ERROR: Sleuth Kit icat executable missing: $::TSKDIR\n"; + exit(1); + } + unless (-x $::TSKDIR . "/istat") { + print "ERROR: Sleuth Kit istat executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/ifind") { + print "ERROR: Sleuth Kit ifind executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/ils") { + print "ERROR: Sleuth Kit ils executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/fls") { + print "ERROR: Sleuth Kit fls executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/ffind") { + print "ERROR: Sleuth Kit ffind executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/blkcat") { + print "ERROR: Sleuth Kit blkcat executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/blkcalc") { + print "ERROR: Sleuth Kit blkcalc executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/blkls") { + print "ERROR: Sleuth Kit blkls executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/img_stat") { + print "ERROR: Sleuth Kit img_stat executable missing\n"; + exit(1); + } + unless (defined $::FILE_EXE) { + print "ERROR: File executable location not defined in config file\n"; + exit(1); + } + unless (-x "$::FILE_EXE") { + print "ERROR: File executable ($::FILE_EXE) missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/fsstat") { + print "ERROR: Sleuth Kit fsstat executable missing\n"; + exit(1); + } + unless (defined $::MD5_EXE) { + print "ERROR: MD5 executable location not defined in config file\n"; + exit(1); + } + unless (-x "$::MD5_EXE") { + print "ERROR: md5 executable ($::MD5_EXE) missing\n"; + exit(1); + } + if ($::SHA1_EXE ne "") { + unless (-x "$::SHA1_EXE") { + print "ERROR: sha1 executable missing\n"; + exit(1); + } + } + unless (-x $::TSKDIR . "/srch_strings") { + print "ERROR: Sleuth Kit srch_strings executable missing\n"; + exit(1); + } + + if ($::LIVE == 0) { + unless (-x $::TSKDIR . "/sorter") { + print "ERROR: Sleuth Kit sorter executable missing\n"; + exit(1); + } + unless (-x $::TSKDIR . "/hfind") { + print "ERROR: Sleuth Kit hfind executable missing\n"; + print + " You likely have an old version of The Sleuth Kit or TASK\n"; + exit(1); + } + } + + unless (-x "$GREP_EXE") { + print "ERROR: grep executable missing\n"; + exit(1); + } +} + +# check values that should be defined in the configuration files +# This will show incomplete installations +sub check_vars { + unless ((defined $::TSKDIR) && ($::TSKDIR ne "")) { + print "ERROR: TSKDIR variable not set in configuration file\n"; + print " This could been caused by an incomplete installation\n"; + exit(1); + } + + unless (-d "$::TSKDIR") { + print "Invalid Sleuth Kit binary directory: $::TSKDIR\n"; + exit(1); + } + + return if ($::LIVE == 1); + + # Verify The evidence locker directory + unless ((defined $LOCKDIR) && ($LOCKDIR ne "")) { + print "ERROR: LOCKDIR variable not set in configuration file\n"; + print " This could been caused by an incomplete installation\n"; + exit(1); + } + + unless (-d "$LOCKDIR") { + print "Invalid evidence locker directory: $LOCKDIR\n"; + exit(1); + } +} diff --git a/base/make-live-cd.base b/base/make-live-cd.base new file mode 100644 index 0000000000000000000000000000000000000000..08c66e9f373194ca13ed38ce33dd114643ef7f7a --- /dev/null +++ b/base/make-live-cd.base @@ -0,0 +1,153 @@ +# +# This makes a directory ($CD) with the needed files to burn to +# a CD for live analysis +# +# Current limitations are that Perl needs to be on the suspect system and +# that it uses the untrusted Perl files. + +require 'conf.pl'; +use vars '$USE_STIMEOUT', '$STIMEOUT', '$CTIMEOUT', '$SAVE_COOKIE'; +use vars '$GREP_EXE', '$TSKDIR'; + + +my $CD = "./live-cd/"; + +# Make the directories +if (-d "$CD") { + print "Live CD directory already exists ($CD)\n"; + print "Plese delete and run this again\n"; + exit (1); +} + +print "Making base directory ($CD)\n"; +die "Error making Live CD directory ($CD)" + unless (mkdir "$CD", 0775); + +die "Error making Live CD binaries directory ($CD)" + unless (mkdir "$CD/bin/", 0775); + + +print "Copying executables\n"; + +# Copy the executables +die "Missing grep executable ($GREP_EXE)" + unless (-x "$GREP_EXE"); +`cp '$GREP_EXE' '$CD/bin/grep'`; +die "Error copying grep executable" + unless (-x "$CD/bin/grep"); + + +die "Missing MD5 executable ($::MD5_EXE)" + unless (-x "$::MD5_EXE"); +`cp '$::MD5_EXE' '$CD/bin/md5sum'`; +die "Error copying MD5 executable" + unless (-x "$CD/bin/md5sum"); + +# Sleuth Kit Binaries +die "Missing Sleuth Kit Directory ($TSKDIR)" + unless (-d "$TSKDIR"); + +foreach my $exec ("blkcalc", "blkcat", "blkls", "blkstat", "ffind", "fls", "fsstat", + "icat", "ifind", "ils", "istat", "srch_strings", "img_stat", "mmls") { + + die "Missing Sleuth Kit executable ($exec)" + unless (-x "$TSKDIR/$exec"); + + `cp '$TSKDIR/$exec' '$CD/bin/$exec'`; + + die "Error copying Sleuth Kit executable ($exec)" + unless (-x "$CD/bin/$exec"); +} + + +# Make a fake file +open FILE, ">$CD/bin/file" or die ("Error creating Live CD file exec"); +print FILE "#!./bin/perl\n"; +print FILE "print STDOUT \"File Type Not Supported During Live Analysis\n\";\n"; +close FILE; +`chmod +x "$CD/bin/file"`; + + +# Copy the autopsy directories +print "Copying autopsy files\n"; +`cp -r help "$CD"`; +`cp -r lib "$CD"`; +`cp -r pict "$CD"`; + + +# Get the path for Perl from the current autopsy +open AUT, "<./autopsy" or die ("Error opening normal autopsy exec"); +my $perl; +while (<AUT>) { + $perl = $_; + last; +} +close AUT; + +if ($perl =~ /^#!(\S+)/) { + $perl = $1; +} else { + die "Error parsing Perl location from autopsy" +} + + +# Copy the perl exec +# @@@ I'm not sure if just copying the bin is enough ... +die "Missing Perl executable ($perl)" + unless (-x "$perl"); + +`cp '$perl' '$CD/bin/perl'`; + +die "Error copying perl executable" + unless (-x "$CD/bin/perl"); + + +# Make a new autopsy +open AUT, ">$CD/autopsy" or die ("Error opening Live CD autopsy exec"); + +print AUT "#!./bin/perl -wT\n"; +print AUT "use lib '.';\n"; +print AUT "use lib './lib/';\n"; + + +open BASE, "<./base/autopsy.base" or die ("Error opening base autopsy"); + +print AUT $_ + while (<BASE>); + +close (AUT); +close (BASE); + +`chmod +x "$CD/autopsy"`; + + +print "Creating configuration file using existing settings\n"; + +# Make the configuration file +open CONF, ">$CD/conf.pl" or die ("Error opening Live CD Config file"); + +print CONF "# Configuration file for Live CD version of Autopsy\n"; +print CONF "# http://www.sleuthkit.org/autopsy\n"; +print CONF "# Created on ".localtime()."\n\n"; + +# Variables +print CONF "\$USE_STIMEOUT = $USE_STIMEOUT;\n"; +print CONF "\$STIMEOUT = $STIMEOUT;\n"; +print CONF "\$CTIMEOUT = $CTIMEOUT;\n"; +print CONF "\$SAVE_COOKIE = $SAVE_COOKIE;\n"; + +print CONF "\n"; +print CONF "\$INSTALLDIR = './';\n"; +print CONF "\$NSRLDB = '';\n"; +print CONF "\$LOCKDIR = './read-only-live-version/';\n"; + +print CONF "\n"; +print CONF "# System Utilities\n"; +print CONF "\$GREP_EXE = './bin/grep';\n"; +print CONF "\$FILE_EXE = './bin/file';\n"; +print CONF "\$MD5_EXE = './bin/md5sum';\n"; +print CONF "\$TSKDIR = './bin/';\n"; + +close CONF; + +print "\n"; diff --git a/configure b/configure new file mode 100755 index 0000000000000000000000000000000000000000..72d5c84f009eea439cd578d35c49db714d8e8662 --- /dev/null +++ b/configure @@ -0,0 +1,475 @@ +#!/bin/sh + +# Minimum version of TSK that is required +minver="3.1.0"; + +# The last released version of TSK +curtskver="3.1.1"; + +# Configuration script for the Autopsy Forensic Browser +# +# Brian Carrier [carrier@sleuthkit.org] +# +# Copyright (c) 2003-2008 by Brian Carrier. All rights reserved +# +# Copyright (c) 2001-2003 by Brian Carrier, @stake. All rights reserved +# +# Copyright (c) 2001 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Directories to search in +dirs='/usr/local/bin/ /usr/bin/ /usr/ccs/bin/ /bin/ /usr/ucb/bin/ /sbin/ /usr/sbin/ /usr/local/sbin/' + + +echo "" +echo " Autopsy Forensic Browser Installation" +echo "" + + + +############################################################################# +# create conf.pl +############################################################################# + + +conf='conf.pl' + +rep="" +if (test -f $conf) then + echo "A configuration file already exists, overwrite? (y/n):"; + read rep; +else + rep="y" +fi + +if (test "$rep" = "y") then + + +# First add the variables that are static +# +# DEFAULT USER SETTINGS +# +echo '# Autopsy configuration settings' > $conf +echo '' >> $conf +echo '# when set to 1, the server will stop after it receives no' >> $conf +echo '# connections for STIMEOUT seconds. ' >> $conf +echo '$USE_STIMEOUT = 0;' >> $conf +echo '$STIMEOUT = 3600;'>> $conf + +echo '' >> $conf +echo '# number of seconds that child waits for input from client' >> $conf +echo '$CTIMEOUT = 15;' >> $conf + +echo '' >> $conf +echo '# set to 1 to save the cookie value in a file (for scripting)' >> $conf +echo '$SAVE_COOKIE = 1;' >> $conf + + +############################################################################# +# INSTALLATION DIRECTORY +############################################################################# +echo '' >> $conf; +echo \$INSTALLDIR = \'$PWD/\'\; >> $conf; + + +# Now add the variables that need user interaction + +# +# FIND THE UTILITIES +# +echo '' >> $conf +echo '' >> $conf +echo '# System Utilities' >> $conf + + + +# +# GREP +# +echo '' +echo '---------------------------------------------------------------' +echo '' +found=0 +for d in $dirs + do if (test -x ${d}grep) then + echo \$GREP_EXE = \'${d}grep\'\; >> $conf; + echo "grep found: ${d}grep"; + grepexe="${d}grep"; + found=1; + break; + fi; +done + +# Prompt if not found +if (test $found -eq 0) then + echo 'ERROR: grep utility not found'; + echo 'Enter location of executable:'; + while (test 1 -eq 1) + do read grepexe; + if (test -x "$grepexe") then + echo \$GREP_EXE = \'$grepexe\'\; >> $conf; + break; + else + echo 'grep was not found (try again):'; + fi; + done +fi + + +# +# FILE +# +found=0 +for d in $dirs + do if (test -x ${d}file) then + echo \$FILE_EXE = \'${d}file\'\; >> $conf; + echo "file found: ${d}file"; + found=1; + break; + fi; +done + +# Prompt if not found +if (test $found -eq 0) then + echo 'ERROR: file utility not found'; + echo 'Enter location of executable:'; + while (test 1 -eq 1) + do read fileexe; + if (test -x "$fileexe") then + echo \$FILE_EXE = \'$filexe\'\; >> $conf; + break; + else + echo 'file was not found (try again):'; + fi; + done +fi + +# +# MD5 +# +found=0 +for d in $dirs + do if (test -x ${d}md5) then + echo \$MD5_EXE = \'${d}md5\'\; >> $conf; + echo "md5 found: ${d}md5"; + found=1; + break; + elif (test -x ${d}md5sum) then + echo \$MD5_EXE = \'${d}md5sum\'\; >> $conf; + echo "md5 found: ${d}md5sum"; + found=1; + break; + fi; +done + +# Prompt if not found +if (test $found -eq 0) then + echo 'ERROR: md5/md5sum utility not found'; + echo 'Enter location of executable:'; + while (test 1 -eq 1) + do read md5exe; + if (test -x "$md5exe") then + echo \$MD5_EXE = \'$md5exe\'\; >> $conf; + break; + else + echo 'md5 was not found (try again):'; + fi; + done +fi + + +# +# SHA-1 +# +found=0 +for d in $dirs + do if (test -x ${d}sha15) then + echo \$SHA1_EXE = \'${d}sha\'\; >> $conf; + echo "sha1 found: ${d}sha1"; + found=1; + break; + elif (test -x ${d}sha1sum) then + echo \$SHA1_EXE = \'${d}sha1sum\'\; >> $conf; + echo "sha1 found: ${d}sha1sum"; + found=1; + break; + fi; +done + +if (test $found -eq 0) then + echo 'WARNING: sha1/sha1sum utility not found'; + echo \$SHA1_EXE = \'\'\; >> $conf; +fi; + + + +############################################################################# +# The Sleuth Kit +############################################################################# + +echo '' >> $conf +echo '' >> $conf +echo '# Directories' >> $conf + +echo '' +echo '---------------------------------------------------------------' +echo '' +echo 'Searching for Sleuth Kit Installation.' +found=0 +for d in $dirs + do if ((test -x ${d}fls) && (test -x ${d}ffind) && (test -x ${d}blkstat) && \ + (test -x ${d}blkls) && (test -x ${d}blkcat) && \ + (test -x ${d}mmls) && (test -x ${d}mmstat) && \ + (test -x ${d}fsstat) && (test -x ${d}img_stat) && \ + (test -x ${d}istat) && (test -x ${d}ifind) && \ + (test -x ${d}icat) && (test -x ${d}ils) && \ + (test -x ${d}srch_strings) && \ + (test -x ${d}mactime) && (test -x ${d}sorter)) then + echo \$TSKDIR = \'${d}\'\; >> $conf; + tskdir=${d}; + echo "Found in: ${d}"; + found=1; + break; + fi; +done + +if (test $found -eq 0) then + echo 'Sleuth Kit tools were not found in the standard install locations.' + echo 'If you have not installed them, do so now and configure autopsy again.' + echo 'If you have installed them in a non-standard location, then' + echo ' enter the "bin" directory now:' + + while (test 1 -eq 1) + do read tskdir; + if ((test -x ${tskdir}/fls) && (test -x ${tskdir}/ffind) && (test -x ${tskdir}/blkstat) && \ + (test -x ${tskdir}/blkls) && (test -x ${tskdir}/blkcat) && \ + (test -x ${tskdir}/mmls) && (test -x ${tskdir}/mmstat) && \ + (test -x ${tskdir}/fsstat) && (test -x ${tskdir}/img_stat) && \ + (test -x ${tskdir}/istat) && (test -x ${tskdir}/ifind) && \ + (test -x ${tskdir}/icat) && (test -x ${tskdir}/ils) && \ + (test -x ${tskdir}/srch_strings) && \ + (test -x ${tskdir}/mactime) && (test -x ${tskdir}/sorter)) then + echo \$TSKDIR = \'${tskdir}\'\; >> $conf; + break; + else + echo 'TSK tools were not found or incomplete (try again):'; + fi; + done +fi; + +# Test for latest version +ver=`"${tskdir}/fls" -V | awk '/The Sleuth Kit ver / {print $5}'`; +echo " Version $ver found"; + + +if (test "$ver" '<' "$minver") then + echo "Your version of The Sleuth Kit is not current enough - $minver is needed"; + if (test "$ver" '>' "0.0.0") then + exit 1; + fi; +elif (test "$ver" '<' "$curtskver") then + echo ''; + echo "*** NOTE: A more recent version ($curtskver) of The Sleuth Kit Exists ***" + echo " [Press Enter to Continue]"; + read foo; + +else + echo ' Required version found'; +fi + + + +# NSRL +echo '' +echo '---------------------------------------------------------------' +echo '' +echo 'The NIST National Software Reference Library (NSRL) contains' +echo 'hash values of known good and bad files.' +echo ' http://www.nsrl.nist.gov' +echo '' +echo 'Have you purchased or downloaded a copy of the NSRL (y/n) [n]' +read rep; +if (test "$rep" = "y") then + + echo 'Enter the directory where you installed it:' + while (test 1 -eq 1) + do read nsrldir; + if (test "$nsrldir" = "cancel") then + echo \$NSRLDB = \'\'\; >> $conf; + break; + fi; + if (test -f "${nsrldir}/NSRLFile.txt") then + echo ' NSRL database was found (NSRLFile.txt)'; + echo \$NSRLDB = \'${nsrldir}/NSRLFile.txt\'\; >> $conf; + + if (test -f "${nsrldir}/NSRLFile.txt-md5.idx") then + echo ' NSRL Index file found (NSRLFile.txt-md5.idx)'; + else + echo ' NSRL Index file not found, do you want it created? (y/n) [n]:' + read rep; + if (test "$rep" = "y") then + echo '' + echo '-------------- begin hfind output --------------' + "${tskdir}/hfind" -i nsrl-md5 "${nsrldir}/NSRLFile.txt"; + echo '--------------- end hfind output ---------------' + echo '' + fi; + fi; + break; + else + echo 'The NSRL was not found (the directory should have NSRLFile.txt in it)'; + echo 'Enter a new directory (or cancel to stop):'; + fi; + done +else + echo \$NSRLDB = \'\'\; >> $conf; +fi; + +############################################################################# +# EVIDENCE LOCKER +############################################################################# +mdone=0 +echo '' +echo '---------------------------------------------------------------' +echo '' +echo 'Autopsy saves configuration files, audit logs, and output to the' +echo 'Evidence Locker directory.' +echo '' +echo 'Enter the directory that you want to use for the Evidence Locker:'; +read locker; +if (test -d "${locker}") then + echo " $locker already exists" +else + echo ''; + echo "WARNING: $locker does not exist" + mdone=1 +fi + +echo \$LOCKDIR = \'${locker}\'\; >> $conf; + +fi + +# Start of non-conf.pl file configuration + +############################################################################# +# Setup Perl locations +############################################################################# +found=0; + +for d in $dirs + do if (test -x ${d}perl) then + if (test -n "`${d}perl -v 2> /dev/null | awk '/This is perl/ {print $0}'`") then + ver=`${d}perl -e 'print $];'`; + if (`${d}perl -e 'exit( $] >= 5.008);'`) then + echo "old version of perl found: ${d}perl (version $ver) -- continuing"; + else + echo "perl found: ${d}perl (version $ver)"; + echo "#!${d}perl -wT" > ./config.tmp; + echo "#!${d}perl" > ./config2.tmp; + perlexe="${d}perl"; + found=1; + break; + fi; + fi; + fi; +done + +# If it wasn't found, then prompt for it. +if (test $found -eq 0) then + echo 'ERROR: perl not found or the incorrect version found'; + while (test 1 -eq 1) + do echo 'Enter location of perl executable:'; + read perlexe; + if (test -x "$perlexe") then + if (test -n "`$perlexe -v 2> /dev/null | awk '/This is perl/ {print $0}'`") then + ver=`$perlexe -e 'print $];'`; + if (`$perlexe -e 'exit( $] >= 5.008);'`) then + echo "This version of Perl is too old, 5.8.0 or older needed"; + else + echo "Correct version found"; + echo "#!${perlexe} -wT" > ./config.tmp; + echo "#!${perlexe}" > ./config2.tmp; + found=1; + break; + fi; + else + echo "Perl found, but is not working. Try another"; + fi; + else + echo "file not found"; + fi + done +fi + +# Check if this version of Perl supports large files +if (test -z "`$perlexe -V 2> /dev/null | awk '/USE_LARGE_FILES/ {print $0}'`") then + echo '' + echo ' NOTE: It appears that your Perl does not support large files.'; + echo ' You therefore will not be able to analyze images larger than 2GB.'; + echo ' Download the source version from www.cpan.org and compile a new version.'; + echo " [Press Enter to Continue]"; + read foo; + echo '' +fi; + +# Get current working directory for lib +echo "use lib '$PWD/';" >> ./config.tmp +echo "use lib '$PWD/lib/';" >> ./config.tmp + +if (test -f ./autopsy) then + echo "autopsy already exists, overwrite? (y/n):"; + read rep; + if (test "$rep" = "y") then + cat ./config.tmp base/autopsy.base > ./autopsy + cat ./config2.tmp base/make-live-cd.base > ./make-live-cd + else + echo ' original version was kept'; + fi +else + cat ./config.tmp base/autopsy.base > ./autopsy + cat ./config2.tmp base/make-live-cd.base > ./make-live-cd +fi +chmod 0755 ./autopsy +chmod 0755 ./make-live-cd + +# cleanup +rm -f ./config.tmp +rm -f ./config2.tmp + + +############################################################################# +# CLEANUP +############################################################################# +echo '' +echo '---------------------------------------------------------------' +echo '' +echo "Execute the './autopsy' command to start with default settings." +echo '' + diff --git a/docs/sleuthkit-informer-13.txt b/docs/sleuthkit-informer-13.txt new file mode 100644 index 0000000000000000000000000000000000000000..24d55bb371a330bb63fa5c4142ab9abfe996677d --- /dev/null +++ b/docs/sleuthkit-informer-13.txt @@ -0,0 +1,410 @@ + The Sleuth Kit Informer + + http://www.sleuthkit.org/informer + http://sleuthkit.sourceforge.net/informer + + Brian Carrier + carrier at sleuthkit dot org + + Issue #13 + March 15, 2004 + + + +CONTENTS +-------------------------------------------------------------------- +- Introduction +- What's New? +- Call For Papers +- Did You Know? +- UNIX Incident Verification With Autopsy + + + +INTRODUCTION +-------------------------------------------------------------------- + +I'm almost done! Releasing version 2.00 of Autopsy that is. I'm +polishing up the final changes and documentation and it will be +released later this week. March 19 is the birthday of Autopsy and +The Sleuth Kit's predecessor, TCTutils, also the goal release date +for version 2.00. + +This issue of the Informer will focus on the new live analysis +feature of (the unreleased) Autopsy. This issue also has a lot +entries in the "What's New?" category because we are on a bimonthly +schedule. If you missed getting the February issue of The Informer +and have lessons to share with people then make sure you read the +Call For Papers section so that you can learn how to submit articles +of your own. + + + +WHAT'S NEW? +-------------------------------------------------------------------- +A new version (1.68) of The Sleuth Kit was released. It contains +a couple of bug fixes. There have been discussions on the +sleuthkit-developers list about what new functions should be added +to the next big version 2.00 release. Michael Cohen has been looking +into support for disk images, split images, RAID images, and other +non-raw formats. David Collett has been looking into output formats +for TSK tools so that they can be imported into databases. + +Version 2.00 of Autopsy will be released later this week. I'm +adding the final touches and documentation and the goal is to release +it on March 19, which is the 3 year anniversary of the first release +of Autopsy and TCTutils. Version 2.00 has a new internal design +and has live analysis features (see below article). If you can't +wait for a few days and want one of the beta copies, let me know. + +Guido Metzner has been translating The Sleuth Kit Informer into +German. If anyone else is translating this, let me know and I'll +add a link. + + http://www.guframe.de/sleuthkit.html + + +I started to provide a GPG signature of the source code as it is +released. I have meant to do this for ages. This can help to +ensure that the code being downloaded has not been modified by an +attacker. + + + +CALL FOR PAPERS +-------------------------------------------------------------------- + +I posted a call for papers on the website in late January for +people that are interested in writing articles for the Informer. +Here is the relevant section: + +The Sleuth Kit Informer is looking for articles on open source tools +and techniques for digital investigations (computer / digital +forensics) and incident response. Articles that discuss The Sleuth +Kit and Autopsy are appreciated, but not required. Example topics +include (but are not limited to): + +- Tutorials on open source tools +- User experiences and reviews of open source tools +- New investigation techniques using open source tools +- Open source tool testing results + +http://www.sleuthkit.org/informer/cfp.html + + +Writing these articles takes away from my development time of new +tool features, so any help is appreciated. To keep with the incident +verification theme, any articles on the basics of using 'netstat', +'lsof', etc. to look for signs of an intrusion are also welcome for +the next few issues. If we get enough interest, I'll consider going +back to a monthly schedule. + + + +DID YOU KNOW? +-------------------------------------------------------------------- + +Have you ever noticed that the number of occurrences and locations +of keywords for regular expressions in Autopsy are not always +accurate? This came up on one of the mailing lists this past month +and I'll explain it again here. + +Previous issues of The Informer have covered keyword searching, but +the general idea in Autopsy is that it runs the 'strings' command +on the image file and then uses 'grep' to find the keyword. The +'strings' command returns a long ASCII string that grep examines. +If the keyword is found in the string, then grep fill flag the +string and autopsy will search the string to find the exact location +of the keyword. + +This is easy if a non-regular expression is used, but much more +difficult with regular expressions because 'grep' regular expressions +are different from Perl regular expressions. I do not have a way +to convert the grep regular expression to Perl and therefore I only +return the number of big strings that have the keyword and the +location of the start of the big string. There could be more than +one keyword in the string, in which case the total occurrences value +is too small. The location will also be off because it points to +the start of the large string and not the specific keyword. + +If anyone knows of, or wants to write, a grep to perl regular +expression converter, let me know so that we can update this. + + + +UNIX INCIDENT VERIFICATION WITH AUTOPSY +-------------------------------------------------------------------- + +INTRODUCTION + +For a couple of years, I have been saying that Autopsy can be used +to analyze a live system, but it has taken me a while to make it +EASY to use to analyze a live system. When version 2.0 of Autopsy +is released later this week, you will find that it is much easier +to configure so that it runs on a CD-ROM so that you can examine a +system that is suspected of being compromised. This will allow you +to view files that are hidden by rootkits and will not change the +access times on files that are viewed. + +This article will give an overview of how to use the new features +and what features are available for dead analysis are not for live +analysis. I will also show the future work for Autopsy. This +article uses many of the same concepts as the "UNIX Incident +Verification with The Sleuth Kit" article in issue #10 of the +Informer. + +MOTIVATION + +Some may be asking "why would I want a live analysis feature?". +The primary motivation is for a more automated Incident Verification +procedure. We saw in issue #10 of the Informer, that The Sleuth +Kit can be used to verify an incident because it can show files +that are hidden by rootkits and will not update the access times +on files when they are read. Executing a bunch of command line +tools is tedious though and Autopsy can provide a more automated +investigation procedure. + +The basic scenario is that Autopsy and The Sleuth Kit will be burned +to a CD and inserted into a suspect computer. An investigator will +connect to Autopsy on the suspect UNIX computer with her HTML browser +on a trusted laptop and remotely examine the hard disk contents. +If the computer is found to have been compromised, then it can be +taken offline and acquired using normal procedures. + +Recall from issue #10 that I used two guidelines for incident +verification: + + 1. Minimize the amount of trust that you place in the system so + that more accurate information is collected. + + 2. Minimize the amount of data that you change on the system so + that evidence is preserved. + + +CREATING THE CD + +To satisfy guideline #1 from above, we want to minimize the amount +of trust that we place in the system, so we use our own executables. +At a minimum, we need The Sleuth Kit and Autopsy on the CD and you +will probably have additional tools to examine open network ports +and running processes. This has been the most difficult part of +using Autopsy for live analysis because Autopsy relies on a given +directory structure and locations that it can write to. + +It is now very easy with v2. With v2, you compile TSK and Autopsy +on a similar system to the one that will be investigated just like +you do for a dead analysis. Try the 'make static' with TSK to see +if you can make static executables for your platform. After both +Autopsy and TSK have been compiled, you execute the 'make-live-cd' +command in Autopsy. This script will make a 'live-cd' sub-directory +in the autopsy directory, which contains a copy of autopsy and +copies of TSK executables, grep, strings, perl etc: + + # ./make-live-cd + Making base directory (./live-cd/) + Copying executables + Copying autopsy files + Creating configuration file using existing settings + + +The 'live-cd' directory has a 'bin' directory where additional +executables can be copied to and then the whole directory can be +burned to a CD. + + +BASIC USAGE + +After the CD has been created and there is a system suspected of +being compromised, then it is time to take advantage of the new +features. There are two scenarios for live analysis. The first +scenario uses a network share from a trusted system that you can +write to. In this case, autopsy is run as normal and you specify +the evidence locker directory as the mounted disk. The evidence +locker is specified with '-d': + + # ./autopsy -d /mnt/ev_lock 10.1.32.123 + +The above would start autopsy, use '/mnt/ev_lock/' as the evidence +locker and would allow connections from 10.1.32.123 (where the +investigator would connect from using an HTML browser). Remember that +we do not want to write to the suspect system, so we should only use +a network share and not a local directory in this scenario. + +The second scenario does not use an evidence locker and does not +intentionally write any data to disk. This scenario does not need +the network share and each of the devices (or partitions) that will +be analyzed are specified on the command line using the '-i' flags. +The '-i' flag requires three arguments: the device, the file system +type, and the mounting point. For example, to examine the '/dev/hda5' +and '/dev/hda8' partitions on a Linux system, the following could +be used: + + # ./autopsy -i /dev/hda5 linux-ext3 / -i /dev/hda8 linux-ext3 /usr/ \ + 10.1.32.123 + +The file system type must be one of the types that are supported +by TSK. The remote IP address must also be given, otherwise you +will have to use a browser on the suspect system and that will write +data to the disk. + +When you use the '-i' flag, then autopsy will start in the 'Host +Manager' view where you can select the image that you want to +analyze. You will skip the case and host configuration. The default +case name will be 'live', the default host name is 'local', and the +default investigator name is 'unknown'. + + +DIFFERENCES WITH DEAD ANALYSIS + +There are some features that are not available for live analysis +because they write files to the disk. In this section, I am using +the term live analysis for the scenario where there is not a mounted +network share and there is not evidence locker. + +No auditing is performed during a live analysis because there is +no where to write the logs. In the future, if this is needed, then +a method of writing logs to a floppy disk could be configured. +Timelines of file activity cannot be created because they need to +create files. Hash databases are also not currently used, although +they could in the future. It may be difficult to maintain the +latest hash database on the same CD as the latest autopsy and TSK +version though. Notes and event sequencer notes cannot be created. + +Keyword searches can be performed, but the strings file and unallocated +only search cannot be performed because they both require a file +to be created. The search results are also not cached. You can +also not sort files by their type (all executables, all pictures +etc.). In many cases, you would not want to do many of these +operations because they are time intensive and you are generally +looking for obvious and quick evidence during the incident verification. +If you need theses features, then you can use the network share +scenario. + + +SIMILARITIES WITH DEAD ANALYSIS + +Now that we know what is different, I'll cover what is the same. +All of the file mode analysis is the same, except that you cannot +get the file type. The method that is used to compile 'file' in +TSK does not allow autopsy to easily move it around. This behavior +may change in the future. Meta data, data unit, and file system +mode all work as usual. + +You can still export file contents to your trusted laptop because +it uses the HTTP to save the file. So, you can select the export +button on a log file to save a copy. You can also generate ASCII +reports and save them to your local analysis station. + + +GUIDELINE COMPLIANCE + +All of these features may sound great, but lets examine how well +the new Autopsy satisfies the two guidelines. I think it is always +important to compare a new tool or technique against generic +guidelines. + +The first guideline was about not trusting the local system. No +software-based live analysis tool can be 100% independent from the +local system because it always needs to request service from the +operating system. With Autopsy, we use our own copies of the Autopsy +perl code, TSK executables, grep, and strings. + +There is one large problem with the current Autopsy model and it +is because of Perl. Perl is a beast and contains many modules and +libraries. When the 'make-live-cd' script is run, a copy of the +'perl' executable is copied to the 'live-cd' directory, but that +is it. The executable will still need the modules and libraries +on the local system. Solving this may require building a special +version of Perl that is more static. I need to investigate this +more. + +Another method of getting around this limitation is to use the +'perl2exe' program that converts a Perl script to a dynamic executable. +The resulting executable will still rely on dynamic libraries on +the suspect system, but it seems a little cleaner. The perl2exe +program is a commercial tool. + +One of the benefits of the Autopsy design, is that Autopsy did not +exist on the system when it was suspected of being compromised. +Therefore, the attacker will not know that Autopsy will be used to +investigate the system and therefore will be less likely to tamper +with its dependencies. If the investigation tools are running on +the system during the attack, then I would guess that the attacker +will be more likely to tamper with them. + +The second guideline was about modifying data on the system. I +described the features that were removed in the previous section. +This was easy to do because there were functions that write the +logs or notes. Each of those starts with a check to see if logging +is enabled. Timelines and file type sorting were also disabled +because it required writing to disk. + +I can never guarantee that no data will be written from a live +analysis. Memory will be overwritten when the software is loaded +and the access times on dynamic libraries will updated. Running +the tools may require the operating system to write memory to the +page file, which may overwrite data from the attacker. + +This is a shameless plug, but for those that are interested in live +analysis, then you may want to check out a paper that a colleague, +Joe Grand (author of pdd), and I wrote about a hardware device to +acquire memory from a live system. It does not overwrite memory +because there is no process to load and it access the physical +memory directly so it does not rely on the local operating system. +It was recently published in the new Journal of Digital Investigation +(reference is below and that issue of the journal is currently free +to view). + + + +FUTURE WORK + +There is always more to do, and I'll cover that here. + +I would like to also include other system utilities, such as 'ps' or +'netstat' on the CD and allow the Autopsy user to run the commands +from within autopsy. This should be fairly easy, but the difficult +part is that many of the operating systems use different flags for +different tools. + +I would like to start incorporating scripts into the tool (for both dead +and live analysis). This will allow you to more easily detect rootkits +or other signatures. + +The biggest thing that needs future work is using Perl. Autopsy +still relies on the version of Perl on the suspect system and that +could run into problems if an attacker modifies it. Using the +perl2exe tool can reduce the risk, but it is a commercial tool. + + +CONCLUSION + +The live analysis mode of Autopsy allows you to more easily analyze +a system that is suspected of being compromised. It can also be +used to examine a honeypot. Live analysis is not ideal because you +are stilly relying on the suspect operating system for data, but +it is required in some situations. More work needs to be done with +Autopsy so that it depends less on the local system. + + + +REFERENCES +Autopsy + http://www.sleuthkit.org/autopsy/ + +perl2exe + http://www.indigostar.com/perl2exe.htm + +Sleuth Kit + http://www.sleuthkit.org/sleuthkit + +Sleuth Kit Informer #10 + http://www.sleuthkit.org/informer/sleuthkit-informer-10.html + +A hardware-based memory acquisition procedure for digital investigations + Brian D. Carrier and Joe Grand + Volume 1, Issue 1 - Journal of Digital Investigations + http://www.sciencedirect.com/science/journal/17422876 + +-------------------------------------------------------------------- +Copyright (c) 2004 by Brian Carrier. All Rights Reserved diff --git a/global.css b/global.css new file mode 100644 index 0000000000000000000000000000000000000000..d44dcca67316383527c078fff37768e0ee044b73 --- /dev/null +++ b/global.css @@ -0,0 +1,19 @@ +h3 { + font-size: 18px; + color: #000000; +} +h2 { + font-size: 20px; + color: #000000; +} +h1 { + font-size: 24px; + color: #000000; +} +body { + font-family: Helvetica, Geneva, Arial, sans-serif; + font-size: 14px; + color: #000000; + #background-color: #dedede; +} + diff --git a/help/blank.html b/help/blank.html new file mode 100644 index 0000000000000000000000000000000000000000..3e40eb24cf48c53e5da913ddad3895ec6ef249e3 --- /dev/null +++ b/help/blank.html @@ -0,0 +1,25 @@ +<HTML> +<HEAD><TITLE>Blank Page</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + + <p> + + <p> + + <p> + <center> + <h3>HELP!</h3> + Here are the help files for using Autopsy. + + <p>In addition to these files, the <tt>Sleuth Kit Informer</tt> + newsletter could be useful.<br> + The Informer typically goes into more technical details than what + is provided here.</p> + + <p><a href="http://www.sleuthkit.org/informer/" target=\"_blank\"> + http://www.sleuthkit.org/informer/</a></p> + + <p>Send documentation updates to <doc-updates at sleuthkit dot org></p> + +</BODY> +</HTML> diff --git a/help/caseman.html b/help/caseman.html new file mode 100644 index 0000000000000000000000000000000000000000..ee4fc315b86143faf05256139ada72f69a1b043d --- /dev/null +++ b/help/caseman.html @@ -0,0 +1,161 @@ +<HTML> +<HEAD><TITLE>Autopsy Case Management Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + + +<CENTER><H2>Case Management</H2></CENTER> +<P> +<H3>Overview</H3> +Autopsy organizes images based on the case and host that they came +from. A case contains one or more hosts (a new case should be +created for each investigation). Each host can contain one or more +images, which correspond to disks or partitions on the host. + + +<P> +<H3>Creating a New Case</H3> +From the Main Menu (at startup) select <U>New Case</U>. You will +have to enter the case name and an optional short description. +The case name must be a valid directory name (no spaces - no +symbols). A list of investigators will also be requested. These +will be used for the audit logs, not for authentication. A directory +with the same name as the case will be created in the Evidence +Locker. To later rename the case, simply rename the directory. + +<P> +For example: +<TABLE CELLSPACING=8> +<TR> + <TD>Case Name:</TD><TD><TT>bankofmars</TT></TD> +</TR> +<TR> + <TD>Case Description:</TD><TD><TT>Theft of $1,000,000,000.01 from The Bank of Mars</TT></TD> +</TR> +<TR> + <TD>Investigators:</TD><TD><TT>gadget</TT></TD> +</TR> +</TABLE> + +<P> +<H3>Adding a New Host</H3> +A Host must then be created in the Case. Select the Case that was +just created from the Case Gallery and enter the Host Gallery. +Select <U>Add Host</U> and enter the host name, a short description, +and time information such as time zone and clock skew. The clock +skew is how many seconds the system was off from a synchronized +clock. Adding a host will create a directory in the case directory +and subdirectories in the host for the images, output data, logs, +and reports. If you do not add a time zone, then it will default to +the time zone of your analysis system. A list of time zones can be +found <a href="timezones.html">here</a>. + +<P> +You can optionally add the path to <A HREF="hash_db.html">hash databases</A>. + +<P> +For example, the 'Bank of Mars' incident could have two hosts +involved: + +<TABLE CELLSPACING=8> +<TR> + <TD>Host Name:</TD><TD><TT>db_server</TT></TD> +</TR> +<TR> + <TD>Host Description:</TD><TD><TT>Main Database Server - Solaris</TT></TD> +</TR> +<TR> + <TD>Timezone:</TD><TD><TT>EST5EDT</TT></TD> +</TR> +<TR> + <TD>Timeskew:</TD><TD><TT>-100</TT></TD> +</TR> +<TR> + <TD>Known Good Database:</TD><TD><TT>none</TT></TD> +</TR> +<TR> + <TD>Known Bad Database:</TD><TD><TT>none</TT></TD> +</TR> +</TABLE> + +<P> +<TABLE CELLSPACING=8> +<TR> + <TD>Host Name:</TD><TD><TT>file_server</TT></TD> +</TR> +<TR> + <TD>Host Description:</TD><TD><TT>Windows File Server - Win 2k</TT></TD> +</TR> +<TR> + <TD>Timezone:</TD><TD><TT>CST6CDT</TT></TD> +</TR> +<TR> + <TD>Timeskew:</TD><TD><TT>0</TT></TD> +</TR> +<TR> + <TD>Known Good Database:</TD><TD><TT>/usr/local/forensics/hash/win2k.txt</TT></TD> +</TR> +<TR> + <TD>Known Bad Database:</TD><TD><TT>/usr/local/forensics/hash/win_hack.txt</TT></TD> +</TR> +</TABLE> + +<P> +<H3>Adding a New Image</H3> +Next, images must be added to the host. Select the host that was +just added from the Host Gallery and enter the Host Manager. Select +<U>Add Image File</U> and a new form is shown. The first text box in +the form is for the path of the image file. If you are importing a +split image, then the extension must be ordered based on the file order. +Supply a '*' in the file name extension where the numbers or letters are. +(i.e. .../image.*). The image file can be +of a full disk or of an individual partition. You must select which +it is though. Before they can analyzed, the images will have to +be located in the evidence locker. You are given a choice to either +create a symbolic link from the current location, to copy the file, +or to move the file from its current location to the host directory. +Select the desired import method. For example: + +<TABLE CELLSPACING=8> +<tr><td>Image Path:</TD><TD><TT>/mnt/sys1/disk2.*</TT></TD></TR> +<tr><td>Type:</td><td><tt>Disk</tt></td></tr> +<tr><td>Import Action:</TD><TD><TT>symlink</TT></TD></TR> +</table> + +<p> +If you are importing a split image, then the next window will confirm the +order of the images. After that, the next window will allow you to specify +or calculate the MD5 for the file. This should be of the full file and if you +are importing a split image then it should be for all files combined. +If you are importing a volume image, then Autopsy will try to determine the +file system type. You will also need to specify the mounting point. This is used for cosmetic purposes only when printing the full path of files. + +<p> +If the image file is a disk image then Autopsy will list all of the partitions and try to determine the file system in each one. You have the option to not import a partition and to change the file system type. + +<P> +<H3>MD5 Values</H3> +Each host has an <TT>md5.txt</TT> file that contains +the MD5 value for files in that directory. Autopsy uses that file +to validate the integrity of files. By default, when a file is +imported into Autopsy, its MD5 will be calculated. If it is already +known, then it can be entered in the 'Add Images' window. + + +<P> +<H3>Host Subdirectories</H3> +Each host has an <TT>images</TT> directory and an <TT>output</TT> +directory. All data generated by Autopsy is saved to the <TT>output</TT> +directory. The theory behind this design, was to allow the <TT>images</TT> +directory to have strict permissions to prevent accidently modifying +the images. Therefore, the <TT>images</TT> directory can have its write +bits removed to prevent modifications. + +<p> +<h3>References</h3> +Issue 2 of <a href="http://www.sleuthkit.org/informer/" target=\"_blank\">The Sleuth Kit Informer</a> discusses case management and how to break a disk image into file system images. + + + +<P><HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/data_mode.html b/help/data_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..db2d20eafd32959ad8fbd18349940d7e55f03a16 --- /dev/null +++ b/help/data_mode.html @@ -0,0 +1,97 @@ +<HTML> +<HEAD><TITLE>Autopsy Data Unit Analysis Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> +<CENTER><H2>Data Unit Analysis</H2></CENTER> + + +<H3>Overview</H3> +<P> +The Data Analysis Mode allows an investigator to view the contents +of an individual data unit. Data units is a generic term used to +describe the areas on the disk that are used to store data. Each +file system calls the data unit a different thing (i.e. Fragments +or Clusters). This mode is most useful when recovering and analyzing +deleted data. + +<H3>Input</H3> +<P> +To view the contents of a specific address, enter it into the text +box on the left-hand side. By default, only one data unit will be +displayed. To view more than one consecutive unit, enter the +number in the text box below. + +<P> +It is common to extract the unallocated space from a file system +image and analyze it for deleted material. The 'blkls' tool in The +Sleuth Kit allows one to extract the data. If interesting data is +found in the 'blkls' file, the next step could be to find its location +in the original image and examine the surrounding data. To do +this, simply calculate which data unit the data was found in (by +dividing the byte offset of the interesting data by the data unit +size (which can be found in <U>Image Details</U>)). Enter that +address into the original text box and select the <U>Unallocated</U> +type. This will find the original location and display it for you. + +<P> +If Autopsy knows about the 'blkls' image, then it can be loaded at any +time by selecting the <U>Load Unallocated</U> button. Then, any +data unit in that file can be examined. + + +<P> +The <B>Lazarus</B> tool was part of <TT>TCT</TT>. It analyzes a chunk +of data and identifies what file type it is and tries to group +consecutive types together. Lazarus numbers its output though starting +with 1. Therefore, instead of subtracting 1 every time you want to view +a data unit identified by Lazarus, simply select the check box. + +<P> +Press the <U>Ok</U> button to display the contents of the address on +the right-hand side of the window. + +<P> +The <U>Allocation List</U> link displays the allocation status of +addresses in intervals of 500. + + +<H3>Viewing</H3> +<P> +After the unit address has been entered, the contents are displayed +in the right-hand side. Filters can be used to view the data in the +desired format (strings, hexdump, ASCII). + +<P> +A report can be generated so that the contents and meta-data about +it will be saved on record. To save the contents locally, press the +<U>Export Contents</U> button. The <U>Add Note</U> button will allow +one to add a comment about the given data unit so that it can be +easily recalled later. + +<P> +The file type is also displayed. This is identified by running +the output through the 'file' command in The Sleuth Kit. + + + +<P> +Autopsy will try to find the meta-data structure that allocated +the unit and display both its address and a file name. This process +is very slow for FAT file systems, so this process is not done by +default during analysis. + +<H3>FAT Notes</H3> +<P> +The Sleuth Kit and Autopsy do not use clusters when dealing with a FAT image. +Only sectors are used. The reason is because FAT does not start +addressing clusters until many sectors into the file system. If +clusters were used to address data units, then there would be no +way to address the sectors in the FAT and secondary FAT. Therefore, +sectors are used for all addresses. NTFS changed the way clusters +were addressed and do not have this problem. See the documentation +in The Sleuth Kit for more details. + + +<P> +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/file_category.html b/help/file_category.html new file mode 100644 index 0000000000000000000000000000000000000000..6665c90b684738933673cbf7568e3526c3cb8abd --- /dev/null +++ b/help/file_category.html @@ -0,0 +1,99 @@ +<HTML> +<HEAD><TITLE>Autopsy File Category Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>File Category Type Analysis Help</H2></CENTER> + +<H3>Overview</H3> +Analyzing large file system images can be very daunting. One way +of identifying files that should be examined is to sort the files based +on file type. This mode of Autopsy will allow one to sort the files +in an image based on type and to exclude known files (i.e. data +reduction). It also allows one to flag files that are known to be bad. + +<H3>Procedure</H3> +The <TT>sorter</TT> document in the <TT>docs</TT> directory of The +Sleuth Kit has more details on the details, but this will provide +an overview of the interface given by Autopsy. + +<P> +The first step is to <U>Sort</U> the image. There are several +options to choose when doing this. The <TT>sorter</TT> tool from +The Sleuth Kit will perform the sorting. There are two major +actions that <TT>sorter</TT> can do: sort files by type and validate +extensions. + +<P> +By default, Autopsy will perform both actions. If you do not want +it to do a given action, deselect it. + + +<P>Within sorting, there are two options: + +<UL> +<LI> The first is to save the output. By default, +details about each file will be added to a category file. For +example, a JPEG image will have the meta data address and image +name saved to the <TT>images</TT> file. By selecting the <U>Save</U> +option, a directory will be created for each category and a copy +of the files will be saved. This could require lots of disk space +(as much as the original image size). + +<LI> The second option is to save unknown file types. There are +configuration files that contain rules about common data types. If +a file is encountered that does not have a rule, it is added to an +<TT>unknown</TT> file. If this is not desired, select the <U>Do Not +Save Unknown</U> option. +</UL> + +<P> +During the sorting process, the <TT>sorter</TT> tool will also examine +the extension of the file. If the file type is known, it has known +extensions, and the file does not have one of those extensions, it will +be added to a <TT>mismatch</TT> file. This can be deselected if it is +not wanted. + + +<H3>Hash Databases</H3> +One easy way of data reduction is to use hash databases. The <TT>sorter</TT> +tool can use three different hash databases. Each can be configured +within Autopsy and used in other screens. + +<UL> + <LI><B>NIST NSRL</B>: The NIST NSRL contains hashes of trusted operating + systems and programs. This is used to ignore known files. Files found + in the NSRL will not be included in the file categories (to save time + when reviewing the files). If the file is in the NSRL and has an + extension mismatch, it will be noted in a special file. + + <LI><B>Ignore Database</B>: This database must be created by the user + and added to the host. It is similar to the NSRL in that it contains + hashes of known good files. They will be ignored in the same way that + those from NSRL are. + + <LI><B>Alert Database</B>: This database must also be created by the + user and added to the host. It contains hashes of files that are + known to be bad and should identified if found in the image. This would + include known rootkits or photographs. Hits from this databases are + found in the <TT>alert</TT> file. +</UL> + +<P> +More details can be found in the <A HREF="hash_db.html">Hash +Database</A> Help. + +<H3>Output</H3> +Currently, there is no way to view the output from within Autopsy. +All data can be found in the <TT>output</TT> directory of the host. +A directory is created for the <TT>sorter</TT> output. View the +<TT>index.html</TT> file and it contains links to the other files. + +<p> +<h3>References</h3> +Issues 3, 4, and 5 of <a href="http://www.sleuthkit.org/informer/" target=\"_blank\">The +Sleuth Kit Informer</a> discussed using the 'sorter' tool. + + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/file_mode.html b/help/file_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..ca39d1d342f0cfca44fd377ec0547e21105a28c3 --- /dev/null +++ b/help/file_mode.html @@ -0,0 +1,221 @@ +<HTML> +<HEAD><TITLE>Autopsy File Analysis Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>File Analysis</H2></CENTER> +<H3>Overview</H3> +The File Analysis mode allows one to analyze an image from the file +and directory perspective. This provides the same interface that users +typically use with a normal computer. This mode will also display +information about deleted files though. + +<P> +This mode will allow one to examine the contents of files and directories +for evidence. It even allows one to perform basic binary analysis by +extracting the ASCII strings from a binary file. The files can also +be sorted by any field. + + +<H3>Directory List</H3> +<P> +The left-hand side window has four main options: +<UL> + <li>Directory Seek + <li>File Name Search + <LI>Hide / Expand Directories + <LI>Show All Deleted Files +</UL> + +<P> +By default, the directory listing is not shown when this mode is +first entered. By select the <U>Expand Directories</U> button, +the full list of directories will be shown. The number of '+' +symbols represent the depth of the directory. As this is an HTML +interface and there is no state management occurring (no cookies +or session id), it would be difficult to have an interface where +one or more directories are expanded yet the rest are collapsed. + +<P> +Selecting the <U>All Deleted Files</U> link will display all of the deleted +files in the image on the right-hand side. + +<P> +There is a text box where a directory (or file) name can be +entered and it will be displayed on the right-hand side. This +makes it easy to jump to a directory without going through the +directory listings. For example, to seek to the 'windows\system32' +folder, you can enter that string into the box instead of scrolling +through the directories. + +<p> +There is also a text box where a pattern of a file name can be +entered and all files that match that pattern will be displayed on +the right-hand side. The search pattern is a Perl regular expression, +so some values, such as '.' or '*' will need to be escaped. The +search is done case insensitive. To find all files that have a JPG +extension, the following could be used "\.jpg". Or to find all files +that begin with a dot, then we could use "^\.". + + +<H3>Directory Contents</H3> +<P> +The window in the upper right-hand side contains the directory +contents. In a file system, a directory allocates data units on +the disk and fills the data units with structures that contain the +name of the file and the address of the meta data structure. This +view parses the file name structures in the directory. It is filled +by either selecting a directory from the left-hand side directory +list or a directory from within the same window. The entries can +be resorted by clicking on any of the header values. + +<P> +The column headers have the following definitions: +<UL> + <LI><B>del</B>: A check in this column represents a deleted file. + See below for a discussion on the different color types of deleted files. + + <LI><B>Type</B>: This column contains the "type" that the file or + directory is. There are two values. The first (<TT>dir</TT>) is the + type according to the directory entry structure. The directory entry + is where the file name is located. The second value (<TT>in</TT>) is + the type according to the meta data structure. + These should be the same except + for deleted files. They are both given to help the investigator + identify if a deleted file has been reallocated or not. For example, + if the meta data structure type is different than the file name + type, then it is likely that one of the structures was reallocated for + a new file. + + <LI><B>Name</B>: The name of the file or directory. Clicking on the + name displays file contents in the bottom window or directory contents + in the same window. For deleted files, no link will exist if the meta + data structure address + has been cleared (which many OS do). To identify the destination of + a UNIX symbolic link, + click on the address at the end of the row. Note that the name + will not be a link if the size of the file is 0 or if the meta data + structure is unknown. + + <LI><B>Modified Time</B>: This column exists for UNIX and NTFS file + systems. It shows the last time that the file data was last modified. + In other words, when was data last written to the data units allocated + by the file. In UNIX, the <TT>utimes()</TT> function can modify this + value vary easily. + + <LI><B>Written Time</B>: This column exists for FAT file systems and + is the time when the file was last written to. Of the three times, + this is the only value that is required by the FAT specification. + + <LI><B>Accessed Time</B>: This column contains the last accessed time of + the file data. On a FAT image, this value is optional and is only + accurate to the day (not hours or seconds). This value can be + modified by the utimes() function in UNIX. + + <LI><B>Changed Time</B>: This column exists for UNIX and NTFS file + systems. It is the last time that the file status (or meta data) was + changed. This is different than the Modified time because modified + deals with the file data and this deals with the descriptive data + in the inode or MFT entry. This value cannot be changed by the + <TT>utimes()</TT> function in UNIX. + + <LI><B>Created Time</B>: This column exists for NTFS and FAT file systems. It + is the time when the file was created. According to the FAT spec, + it is an optional time. + + <LI><B>Size</B>: The size of the file. Note that if the size of the + file is 0, no link will be shown with the name. + + <LI><B>UID</B>: The User ID of the file owner. + + <LI><B>GID</B>: The Group ID of the file owner. + + <LI><B>Meta Data</B>: The file name structures contain a pointer to + the meta data structure that describes the file. This column contains + the address of the structure. Selecting this value + will display the details in the bottom window. + If the value is invalid, then a link will not exist. If it is for + a deleted file name that has a meta data structure with an allocated + status, a "realloc" string will + exist to identify this. + +</UL> +The <U>Add Note</U> link allows you to make a comment about this directory and +have it saved in your personal notes file. + +<P> +The <U>Generate MD5 List</U> +link will generate the MD5 value for every file in the directory and +allow you to save it as a text file. Using fingerprint data bases, +This makes it easy to check for files that were modified by an attacker. + +<P> +The path on top of the window has hyperlinks in it that allow the +user to easily change to a previous directory. + +<P> +There are two different colors used for deleted files. The difference +is based on the status of the data structures in the file. A <FONT +COLOR="red">bright red</FONT> entry means that the file +name data structure is not allocated and the meta data structure +that it points to is also not allocated. This is what we would +expect of a recently deleted file. This means that we can trust +the data we are seeing as long as the meta data structure was not +allocated and unallocated since the deletion. If it is <FONT +COLOR="#800000">darker red</FONT>, then the meta data +structure has been reallocated and the data is most likely not +accurate. + + +<P> +The file size reported by the meta data structure is very important +with The Sleuth Kit. The Sleuth Kit uses this value to identify +how many data units to display. If this size is 0, but the meta +data structure points to data blocks still, they will not be shown. +You can force Autopsy to display the values by selecting the meta +data address and using the 'force' option. + +<P> +To look a file up in one of the <A HREF="./hash_db.html">Hash +Databases</A>, then select the meta data address. That view will +provide an interface to the databases. + +<H3>File Contents</H3> +<P> +The lower right-hand side window displays the contents of a specified +file. The contents can be viewed in either the raw format (which your +browser will not likely display much of if the file is non-ASCII) or +through 'strings'. The strings option is helpful for a quick analysis +of a binary file. + +<P> +Also shown is the file type. This is determined by running the 'file' +command on the output. It uses the magic header and footer values to +guess the file type. If the file type is an image or HTML, an option +will exist to <U>View</U> the data in its interpreted form (i.e. as +a picture or as a web page instead of the raw data). Note that +any HTML that is viewed will be processed in a sanitized environment +that does not load pictures and will not allow one to connect to a +remote site. To view the native picture, select 'Export' and open +the HTML document in another browser. Refer to issue #1 of The Sleuth +Kit Informer for more details on the sanitizing. + +<P> +The <U>Report</U> options create ASCII reports that contain the file +contents as well as data such as MD5 values and dates. + +<P> +The <U>Export</U> button extracts the file out of the image so you can save it +locally and use other tools on it. + +<P> +The <U>Add Note</U> button adds a personal note to the investigator log for +future reference. + +<p> +<h3>References</h3> +Issue 1 of <a href="http://www.sleuthkit.org/informer/" target=\"_blank\">The Sleuth +Kit Informer</a>. + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/fs_mode.html b/help/fs_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..ef8ec4d76073b827e23432def0ec25004a1ff862 --- /dev/null +++ b/help/fs_mode.html @@ -0,0 +1,46 @@ +<HTML> +<HEAD><TITLE>Autopsy Image Details Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Image Details</H2></CENTER> + +<P> +<H3>Overview</H3> +Sometimes there are details about an image that do not correspond to +any file in particular. Those details can likely be found in this +mode. This mode gives the general details of the image and therefore +the contents will vary depending on the file system type. + +<P> +<H3>FFS & EXT2FS</H3> +For the UNIX file systems, this mode will contain the details from +the super block. This generally includes times that the file system +was last mounted and any special flags. It also has the range of +inode addresses and fragment addresses. For advanced file recovery, +you can also identify the group layout and on-disk structure details. +These could be useful for restricting where you search for data. +Files will allocate blocks and fragments in the same Cylinder or +Block group as their inode is in, so your attention can be restricted +to that area. + + +<P> +<H3>FAT</H3> +For FAT file systems, this mode will contain the File Allocation +Table. It will have the cluster runs, which can be selected to +view their contents in <A HREF="data_mode.html">data unit</A> +analysis mode. Or, if the file is fragmented, the pointer can +be selected and the screen will link to the next cluster chain. + + +<P> +<H3>NTFS</H3> +The unique information for an NTFS image is the numerical type +associated with attributes. These values can be dynamic and this +area will identify what they are for that file system. + + +<P> +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/general.html b/help/general.html new file mode 100644 index 0000000000000000000000000000000000000000..5d20c6bbdecbea7906ffbafc2a04ac6889d277c5 --- /dev/null +++ b/help/general.html @@ -0,0 +1,103 @@ +<HTML> +<HEAD><TITLE>General Autopsy Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>General Autopsy Help</H2></CENTER> +<P> +The Autopsy Forensic Browser is a graphical interface to command +line forensics tools and standard UNIX utilities. It allows +you to perform volume and file system analysis on UNIX and Windows systems. + +<P> +All data are saved in a directory in the Evidence Locker, which +was specified at install time or at run time. See +<A HREF="caseman.html">Case Management</A> +for more information. In the normal mode, Autopsy imports an +image file from a disk or partition. In the live mode, Autopsy +can analyze a running system and does not save any data to the +local disk. + + +<P> +The browser has the following modes: +<UL> + +<LI> +<B><A HREF="file_mode.html">Files</A></B>: +Allows you to browse the image file as a file system and view the +contents of files and directories. This mode even shows deleted +file names and Alternate Data Streams in NTFS images. You can sort +the files and directories on meta data. + +<LI><B><A HREF="meta_mode.html"> +Meta Data</A></B>: +Allows you to analyze the image file by examining the meta data structures. +The address of a structure is entered and the details are shown. +This mode is useful for examining unallocated structures and getting +all details about allocated files (including all data units and +other information such as MD5 value). + + +<LI><B><A HREF="data_mode.html"> +Data Unit</A></B>: +Allows browsing by block number. This is most useful when used +with searching or meta data browsing. The contents of the block +can be displayed in ASCII, hex dump, or through <I>strings(1)</I>. +The meta data structure that has allocated the block will be +displayed (if any) along with the file name (if any). + + +<LI><B><A HREF="srch_mode.html"> +Keyword Search </A></B>: +Search an image file using <I>grep(1)</I> for a given string or regular +expression. The result will be a list of data units that have the +string. Each data unit can be selected to view the contents. + + +<LI><B><A HREF="fs_mode.html"> +Image Details</A></B>: +List the details about the file or volume system. The output of +this mode depends on the file system. Examples of the file system +data include the last mount time, the last mount location, and a +detailed break down of block group information or File Allocation +Table contents. + +<LI><B><A HREF="int_mode.html"> +Image Integrity</A></B>: +The integrity of the data can be validated at any +point by selecting this mode. It uses the values in <TT>md5.txt</TT> to +identify if any data have been modified in the analysis process. + +<LI><B><A HREF="tl.html"> +File Activity Timelines</A></B>: +Autopsy can create timelines of file activity based on the Modified, +Access, and Change (Create in FAT/NTFS) times (MAC). The timeline +will contain details about deleted and allocated content. The +resulting timeline can be either viewed within Autopsy or using +other text viewing tools (WARNING: many HTML browsers do not handle +large tables like a timeline very well so using a text editor is +recommended). + +<LI><B><A HREF="file_category.html"> +File Type Categories</A></B>: +Autopsy can sort the files in an image file based on their file type. +For example, all JPEG and GIF files would be identified as images +and all executable files would be identified. This mode will also +ignore files that are found in hash databases of known good files, +identify files that are found in a hash database of known bad files, +and identify files that have an extension that is not consistent +with their file type. + + +<LI><B>Report Generation</B>: +Each of the above browsing techniques allows a report to be generated. +This report lists the date, md5 value, investigator, and other +context information in a text format. This can be used for record +keeping when deleted blocks of data have been found. + +</UL> + + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/grep.html b/help/grep.html new file mode 100644 index 0000000000000000000000000000000000000000..1db60600755c994e752f3d742feff4ab1d716b08 --- /dev/null +++ b/help/grep.html @@ -0,0 +1,70 @@ +<HTML> +<HEAD><TITLE>Autopsy grep Cheat Sheet</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>grep Cheat Sheet</H2></CENTER> + +<H3>Escaped Values</H3> +Autopsy uses the <TT>grep</TT> utility to search an image. grep requires +that some values be "escaped" if they are searched for. Autopsy +will automatically escape those values if the serach is being done for +a <U>non-regular expression</U>. The escaped values include: +<UL> + <LI>\ + <LI>. + <LI>[ + <LI>^ + <LI>$ + <LI>' + <LI>* + <LI>initial - +</UL> + + +<H3>Regular Expressions</H3> +Refer to the man page for 'grep' for more details of creating regular +expressions. Autopsy uses the '-E' flag with 'grep' to specify +extended regular expressions. The following have special meaning +with grep: + +<UL> + <LI><B>[A-Za-z]</B>: Any lower and upper case letter + <LI><B>[:alpha:]</B>: same as above + <LI><B>[0-9]</B>: Any number + <LI><B>[:digit:]</B>: same as above + <LI><B>[0-9A-Za-z]</B>: Any lower and upper case letter or digit + <LI><B>[:alnum:]</B>: same as above + <LI><B>[:space:]</B>: Any white space +</UL> + + +<P> +To specify how many times something can occur, the following are used: +<UL> + <LI><B>?</B>: Optional and can only occur once + <LI><B>*</B>: Optional and can occur more than once + <LI><B>+</B>: Required and can occur more than once +</UL> + +<P> +To specify more than one string to match, use the <B>|</B> operator. + +<H3>Examples</H3> + +<P> +To search for 'Jane Smith' or 'Jack Smith': (Jane)|(Jack) Smith + +<P> +To ensure it matches if a tab is between the first and last name: +(Jane)|(Jack)[:space:]Smith + +<P> +To search for 'Jane Smith' or 'Jane Anne Smith': +Jane( Anne)? Smith + +<P> +or: Jane([:space:]Anne)?[:space:]Smith + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/grep_lim.html b/help/grep_lim.html new file mode 100644 index 0000000000000000000000000000000000000000..d8a29d27e5b272afed6ceffaadbc7fb5d40a1c02 --- /dev/null +++ b/help/grep_lim.html @@ -0,0 +1,75 @@ +<HTML> +<HEAD><TITLE>Autopsy grep Search Limitations</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2><TT>grep</TT> Search Limitations</H2></CENTER> + +<H3>Overview</H3> +<P> +Keyword searches are very basic in Autopsy. Autopsy uses the +<TT>strings</TT> and <TT>grep</TT> tools on the image and when a +hit is found, it uses <TT>ifind</TT> and <TT>ffind</TT> to identify +the file that has allocated the string. This is a very simple and +basic method of searching and is not ideal. This will cause false +positives and will miss data that crosses a fragmented part of a file. +The limitations are outlined in this file. + +<H3>What Will Be Found</H3> +<TT>strings</TT> is first run on the image and the data is passed +to <TT>grep</TT> to do the actual search. This process will find +ASCII and UNICODE strings that are consecutive anywhere in the file. This is +frequently referred to as the physical layout. For example, it +will find strings in the middle of an allocated sector, in an +unallocated sector, in slack space, and in meta data strutures. +This will find a string that crosses sectors, which is good if the +two sectors are for the same file. + +<P> +This technique leads to several types of false positives. For example, +a string that crosses from the allocated space of a file into the slack +space would be found by <TT>grep</TT>. A string that starts in the slack space +and ends in the allocated space of a file will also be found. A string +that crosses sectors of two different allocated files will also be found. The +user must identify if the hit is an actual hit or a false positive. + +<h3>What Will Be Found, but May Be Confusing</h3> +<p> +If you are searching with regular expressions, then the exact +location and number of hits may not be correctly reported. If the +count is incorrect, then it will be too small. If the location +is incorrect, then it will be too early (and could even be in the +next data unit). The reason that this is in accurate is because +the <tt>grep</tt> tool will return a long string to Autopsy that +will contain one or more occurances of the keyword. Autopsy can +not search the long string to find the exact number and location +of the regular expression keywords like it can for non-regular +expression keywords, so it returns only the starting location of +the long string. + +<H3>What Will NOT Be Found</H3> +The biggest category of 'hits' that will not occur using this technique +is strings in a file that cross fragmented data units. For example, consider +a file that has two clusters allocated, cluster 100 and cluster 150. A +string "mississippi" could have "missi" in the final 5 bytes of cluster 100 +and "ssippi" in the initial 6 bytes of cluster 150. The string exists from +the logial level, but not at the physical level. Therefore, the <TT>grep</TT> +search would not find the string. + +<P> +Although not because of <TT>grep</TT>, Autopsy will also not find +data in the slack space during an unallocated-only search. The +extraction tool for The Sleuth Kit (<TT>blkls</TT>) differentiates +between unallocated sectors in FAT and NTFS and slack space. There +is currently no way in Autopsy to extract the slack space and search +it. Autopsy currently only extracts the unallocated sectors and not +the allocated sectors that may have deleted data in them. + + +<H3>Conclusion</H3> +Autopsy has basic keyword search functionality. Future versions may provide +more features and better search results. In the mean time, it is important +that users understand the abilities and limitations of the tool so that they +can be taken into account during the investigation. +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/hash_db.html b/help/hash_db.html new file mode 100644 index 0000000000000000000000000000000000000000..cddb793866172c100d016ab4af401dfa0ec063fa --- /dev/null +++ b/help/hash_db.html @@ -0,0 +1,141 @@ +<HTML> +<HEAD><TITLE>Autopsy Hash Database Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Hash Database Help</H2></CENTER> + +<H3>Overview</H3> +Hash databases are used to quickly identify known good and known +bad files using the MD5 or SHA-1 checksum value. Autopsy uses +three types of hash databases to help the investigator reduce the +number of files that they have to look at. + +<P> +The <B>NIST National Software Reference Library (NSRL)</B> contains +hashes of files that are found in operating systems and software +distributions. These files are <I>known to be good</I> in that they came +from trusted sources and are typically on authorized systems. When +processing files in the image, this database can be used to ignore +files because they are assumed to be known and therefore uninteresting. +The location of this database is configured when Autopsy is installed. +The NSRL must be obtained from NIST at <TT>www.nsrl.nist.gov</TT>. + +<P> +The <B>Ignore Database</B> is a database that the investigator must +create. It is similar to the NIST NSRL in that it contains files +that are <I>known to be good</I> and can be ignored if the user +chooses to do so (only applicable when in <A HREF="file_category.html">File +Type Category Analysis</A>). Examples of files in this category include +system binaries for standard builds. See <A HREF="#db_create">Database +Creation</A> for information on creating this database. Its location +is configured when the host is created and can be edited in the +host configuration file. + +<P> +The <B>Alert Database</B> is a database that the investigator must +create. It contains hashes of <I>known bad</I> files. These are +the files that an investigator wants to know about if they exist +on the system. Examples of this include rootkits or unauthorized +photographs. +When using the <A HREF="file_category.html">File Type Category +Analysis</A>, these files will be saved in a special file. See <A +HREF="#db_create">Database Creation</A> for information on creating +this database. Its location is configured when the host is created +and can be edited in the host configuration file. + + +<H3>Database Uses</H3> +Autopsy uses the hash databases in three ways. + +<UL> + <LI><B><A HREF="file_category.html">File Type Category Analysis</A></B>: The + hash databases are used to identify the <I>known bad</I> files and + ignore the <I>known good</I> files. + + <LI><B><A HREF="meta_mode.html">Meta Data Analysis</A></B>: The hash + databases can be used to identify a file from the meta data view. If + the databases are configured, the hash from a given file can be looked + up by pressing the 'lookup' button. + All three databases can be used in this view. This view can be found + from the File Analysis mode by selecting the meta data address in + the directory listing window. + + <LI><B>Hash Database Manager</B>: From the Host Gallery view, + the Hash Database Manager can be entered. This is where one can + re-index the databases and perform single lookups in any of the + databases. +</UL> + + +<A NAME="db_create"> +<H3>Database Creation</H3> +Currently, Autopsy will only allows one to look entries up in a +hash database. It does not allow one to easily create a database, +but this will describe the process (it is quite simple). + +<P> +Autopsy uses the <TT>hfind</TT> tool from The Sleuth Kit to do the +lookups. This tool requires the database to be indexed so that it +can perform a fast lookup using a binary search algorithm (instead +of a slower sequential search that a tool like grep would do). +When ever a hash database is updated (or created), it must be +indexed. This can be done in Autopsy in the Hash Database Manager +(Note that the database must already be configured though). + +<P> +The NIST NSRL obviously does not have to be created, but it does have +to be indexed before it is used. + +<P> +To make a hash database, we will create a file with the same format +as the <TT>md5sum</TT> command uses. This is just the MD5 hash, +some white space, and the file name. For example:<BR> + + <TT>c4a6761b486de3c6abf7cf2c554289e5 + /bin/ps</TT><P> + +Make a file with this format for every line (it does not have to +be sorted). For example, if you have a trusted system then you can make +a hash database of its system binaries using the following:<BR> + + <TT># md5sum /bin/* /sbin/* > bin-md5.db</TT><P> + +After creation, hash databases must be indexed and sorted (this +includes the NSRL). Databases will be indexed by using the +<TT>hfind</TT> tool. The NSRL database would be indexed with:<BR> + + <TT># hfind -i nsrl-md5 PATH_TO_NSRL/NSRLFile.txt</TT><P> + +A database made by <TT>md5sum</TT> would be indexed with:<BR> + + <TT># hfind -i md5sum bin-md5.db</TT><P> + +Or, if Autopsy has this file configured from when the host was added, +then it can be re-indexed from the Hash Database Manager. + +<H3>Autopsy Configuration</H3> +The alert and ignore databases are stored in the host configuration file +with the headers of 'exclude_db' and 'alert_db'. For example:<BR> + + <TT>alert_db '/usr/local/hash/bad.db'</TT><P> +These entries can be edited at any time. + +<P> +The NSRL database is configured in the <TT>conf.pl</TT> file in the +directory where Autopsy was installed. It has the $NSRLDB variable. +For example:<BR> + + <TT>$NSRLDB = '/usr/local/hash/nsrl/NSRLFile.txt';</TT><P> + +It can be edited, added, and removed at any time (but you must restart +Autopsy). + + +<p> +<h3>References</h3> +Issues 6 and 7 of <a href="http://www.sleuthkit.org/informer/" target=\"_blank\">The +Sleuth Kit Informer</a> discussed hash databases. + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/index.html b/help/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c7c46aec4f12d9142090b5a6ab225f51e5596474 --- /dev/null +++ b/help/index.html @@ -0,0 +1,11 @@ +<HTML> +<HEAD><TITLE>Autopsy Help</TITLE></HEAD> +<LINK REL="SHORTCUT ICON" HREF="../pict/favicon.ico"> +<FRAMESET COLS="20%,*"> + + <FRAME SRC="menu.html"> + <FRAME SRC="blank.html" NAME="cont"> + +</FRAMESET> + +</HTML> diff --git a/help/int_mode.html b/help/int_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..5152b95ad0c2f06368e21ca11b9f2c3b9d05022d --- /dev/null +++ b/help/int_mode.html @@ -0,0 +1,29 @@ +<HTML> +<HEAD><TITLE>Autopsy Integrity Check Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Integrity Check</H2></CENTER> + +<H3>Overview</H3> +It is always important to validate the integrity of images during +an analysis. Autopsy uses the MD5 algorithm to validate images +and other files that are created by Autopsy. + +<P> +The <TT>md5.txt</TT> files contain the MD5 values for files in that +directory. Values are added to it when file system images are +imported into the system or when Autopsy creates the file. This mode +allows one to calculate the MD5 value if it was not created before +and to validate the integrity. + +<P> +When the <U>Image Integrity</U> button is selected from the <U>Host +Manager</U> window, all files will be shown with their MD5 value +if known. Here any image can have its value verified or a new one +created. + + +<P> +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/menu.html b/help/menu.html new file mode 100644 index 0000000000000000000000000000000000000000..8b74a8659454df8443946991561ccef5a26252d2 --- /dev/null +++ b/help/menu.html @@ -0,0 +1,37 @@ +<HTML> +<HEAD><TITLE>Autopsy Help Topics</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<P><A HREF="general.html" TARGET="cont">General Information</A> + +<P><A HREF="caseman.html" TARGET="cont">Case Management</A> + +<HR> + +<P><A HREF="file_mode.html" TARGET="cont">File Analysis</A> + +<P><A HREF="meta_mode.html" TARGET="cont">Meta Data Analysis</A> + +<P><A HREF="data_mode.html" TARGET="cont">Data Unit Analysis</A> + +<P><A HREF="fs_mode.html" TARGET="cont">Image Details</A> + +<P><A HREF="srch_mode.html" TARGET="cont">Keyword Searching</A> + +<P><A HREF="grep.html" TARGET="cont">grep Cheat Sheet</A> + +<P><A HREF="grep_lim.html" TARGET="cont">grep Search Limitations</A> + +<P><A HREF="tl.html" TARGET="cont">Timelines</A> + +<P><A HREF="sequencer.html" TARGET="cont">Event Sequencer</A> + +<P><A HREF="file_category.html" TARGET="cont">File Type Categories</A> + +<P><A HREF="hash_db.html" TARGET="cont">Hash Databases</A> + +<P><A HREF="int_mode.html" TARGET="cont">Image Integrity</A> + +<P><A HREF="timezones.html" TARGET="cont">Time Zones</A> + +</BODY></HTML> diff --git a/help/meta_mode.html b/help/meta_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..3a7475f713bc42ba7e39b7eef519b06de039a277 --- /dev/null +++ b/help/meta_mode.html @@ -0,0 +1,93 @@ +<HTML> +<HEAD><TITLE>Autopsy Metadata Analysis Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Metadata Analysis</H2></CENTER> +<H3>Overview</H3> +The Metadata Analysis mode allows the investigator to view the +details of metadata structures. The metadata structures on the +on-disk structures that contain the details of a file, such as +times and pointers to the allocated data units. FFS and EXT2FS +file systems call them inode structures, NTFS file systems call +them Master File Table (MFT) entries (or File Entries), and the +FAT file system calls them directory entries. This mode is useful +for recovering data and getting a detailed look at a file. + +<H3>Input</H3> +To view the contents of a structure, enter the address in the text +box on the left and select <U>Display</U>. + +<P> +The <U>Allocation List</U> button can also be used to view the +allocation status of metadata structures in groups of 500. + +<H3>Viewing</H3> +The structure details are displayed on the right-hand side. +Typically, the metadata structure does not have the name of the +file that uses that structure, so Autopsy will try to locate the +file name. This process is slow with a FAT file system, so it is +not done by default. + +<P> +The <U>File Type</U> is given, which is the output of the 'file' +tool. This tool uses any header information in the file to +guess what its type is. The MD5 value of the file is also given. + +<P> +If Autopsy has been configured to use hash databases, then one can +select which databases to look for the file in. See +<A HREF="hash_db.html">Hash Databases</A> for more details. + +<P> +The rest of the information will vary depending on the file +system type. In general, the allocation status will be given as +well as the size and each data unit that it has allocated. A +link will exist for each data unit that will show its contents. + +<P> +The <U>Report</U> option generates an ASCII report with the structure +details, MD5 values, and dates in it. The <U>View Contents</U> option +displays the allocated data contents as one large file. The <U>Export</U> +option allows one to save the data contents to a file. The +<U>Add Note</U> button allows one to add a comment about this structure so +that it can be later recalled. + +<H3>NTFS Notes</H3> +<P> +NTFS is a much different design than UNIX file systems and the meta +data structures are addressed differently. They typically have +the form of <TT>A-B-C</TT>, <TT>88-128-3</TT> for example. The +<TT>A</TT> value is the address of the file in the Master File +Table, 88 for example. This is similar to the inode value in UNIX. +Each file has several attributes, including at least one in files +for the data. The <TT>B</TT> value is the type of attribute. In +most cases, the data attribute has a type of 128 so this is commonly +seen. But, if you want to see the file name attribute, you could +specify that type and see the contents if you like (it is fairly +boring). The final value, <TT>C</TT>, is the id. Every attribute +has a unique id value. So, if there are multiple attributes with +the same type, you can specify the type. + +<H3>FAT Notes</H3> +<P> +FAT does not give addresses to the directory entry structures. in +FAT, directory entries can be stored anywhere on the disk. They +are stored in the clusters allocated to the parent directory. This +is unlike NTFS or UNIX where the structures are in a large table +that does not move. get around that, + + +<P> +The addressing issue was solved by providing an address to every +32-byte area in the Data Area. Whether that data was currently a +directory entry or not. This makes it easy to find a given address +and scale when new files are created. The downside is that not +every address is possible, so it is likely that you will see jumps +in the address values. See the documentation in The Sleuth Kit +for more details. + + +<P> +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/sequencer.html b/help/sequencer.html new file mode 100644 index 0000000000000000000000000000000000000000..6afb01e3e74277b06deddbe3897d2ad74face4ce --- /dev/null +++ b/help/sequencer.html @@ -0,0 +1,49 @@ +<HTML> +<HEAD><TITLE>Autopsy Event Sequencer Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Event Sequencer</H2></CENTER> + +<H3>Overview</H3> +<P> +In many investigations, evidence is not found in the order that it was +created during the incident. The notes feature in Autopsy allows one to +make notes about certain files, but it does not help one to put a +series of events in order. + +<P> +The Event Sequencer allows the investigator to make notes and comments +about pieces of evidence. Each note must have a time associated with +it. For files and meta data, the times can be one or more of the +MAC times. Other notes can have times entered manually. The sequencer +will sort the events after each is entered so that the investigator can +quickly identify where there are gaps in the findings. + +<H3>Adding an Event</H3> +<P> +To add an event for a file, directory, or meta data structure, select +the <U>Add Note</U> button. At the bottom will be check boxes that allow +an event to be generated for each of the file's times. The "standard" +note does not have to be generated if it is not needed. + +<P> +To add an event from a different source, go to the Event Sequencer from +the Host Gallery (where the images are listed). At the bottom of +the window will be an area where the new event can be added. The +<B>Source</B> of the event will be shown where the file name of +a file event is normally shown. Examples of this type include +entries from firewall logs or reports from the help desk. + +<H3>Viewing the Sequence Events</H3> +<P> +The <U>Event Sequencer</U> button can be found in the Host Gallery. +This window shows the events that are sorted by the time. Events that +correspond to a file, directory, or meta data structure will have either +[M-Time], [A-Time], or [C-Time] in the note that shows what time this +event was generated from. Clicking on the name will show the contents of +the file or directory. + + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/srch_mode.html b/help/srch_mode.html new file mode 100644 index 0000000000000000000000000000000000000000..43b65791be28ca0292483d84e3e612783f17a15c --- /dev/null +++ b/help/srch_mode.html @@ -0,0 +1,68 @@ +<HTML> +<HEAD><TITLE>Autopsy Keyword Search Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + + +<CENTER><H2>Keyword Search</H2></CENTER> +<H3>Overview</H3> +<P> +This mode searches an image for a given string. This is most useful +when searching for deleted content. To decrease the time required +for a search, a "strings" file can serve as an index. This file +will contain only the ASCII strings in the image. + +<P> +Autopsy will also prompt you to create a file of unallocated data if one +does not exist. This obviously is useful for recovering deleted data. +If a string is found in this file, Autopsy will also report the location +in the original image. + +<H3>Entering the String</H3> + +Enter the string or regular expression into the text box. Autopsy +allows you to search for a either a specific string or using 'grep' +style regular expressions. A case insensitive search will occur +if the appropriate box is checked, otherwise it is case sensitive. +You will also have the option of searching for the string as an +ASCII or a Unicode string. Unicode is much more common in Windows +systems than Unix systems. If both types are selected, then two +searches will be done. + +<P> +If you have not generated a strings file or unallocated data file yet, +that option will exist. + +<P> +The <U>Load Unallocated Image</U> or <U>Load Allocated Image</U> button +exists to switch between the two file types if they have both been +generated. + +<P> +Autopsy also has the ability to perform pre-configured searches. They +are shown in the "Predefined Searches" section. + +<H3>Viewing the Results</H3> +After the image has been searched, a list of "hits" will appear on the +left-hand side. Each data unit that contains the string is listed with +the offset of each occurrence. If a regular expression is used, then the +exact location is not given. + +<P> +If the search was done on an unallocated data file, then an option will +exist next to each address to also view the original. Doing so could +reveal the inode that allocated it. + +<H3>Previous Searches</H3> +The search results are saved to a file so it is easy to recall the +results with out having to perform the search again. + +<H3>Regular Expressions</H3> +You can use grep regular expressions in the search +(refer to the 'grep' <A HREF="grep.html"> +help page</A> and man page for more details). To search for +a couple of different words you would use: <TT>(foo) | (bar)</TT>. + + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/temp.html b/help/temp.html new file mode 100644 index 0000000000000000000000000000000000000000..c3a4b95ee9d59917f09893d38bb8e66335bc88fe --- /dev/null +++ b/help/temp.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD><TITLE>Autopsy File Analysis Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + + + + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/timezones.html b/help/timezones.html new file mode 100644 index 0000000000000000000000000000000000000000..6fa95b7c2151798e543c22b1bf30b57d288f0ed9 --- /dev/null +++ b/help/timezones.html @@ -0,0 +1,508 @@ +<HTML> +<HEAD><TITLE>Autopsy File Analysis Help - Time zone</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<p> + +When creating a host, you can specify a time zone. If you do not, it will default to the time zone to which your system is set. If the system being investigated came from a different time zone, then you need to figure out what it was called. Below are a list of time zones that are known by most computers. + +<p><br> +This list was copied from this site: +<tt>http://showtunepink.com/ical/TIMEZONES</tt> + +<pre> + +Africa/Addis_Ababa +Africa/Algiers +Africa/Asmera +Africa/Bangui +Africa/Blantyre +Africa/Brazzaville +Africa/Bujumbura +Africa/Cairo +Africa/Ceuta +Africa/Dar_es_Salaam +Africa/Djibouti +Africa/Douala +Africa/Gaborone +Africa/Harare +Africa/Johannesburg +Africa/Kampala +Africa/Khartoum +Africa/Kigali +Africa/Kinshasa +Africa/Lagos +Africa/Libreville +Africa/Luanda +Africa/Lubumbashi +Africa/Lusaka +Africa/Malabo +Africa/Maputo +Africa/Maseru +Africa/Mbabane +Africa/Mogadishu +Africa/Nairobi +Africa/Ndjamena +Africa/Niamey +Africa/Porto-Novo +Africa/Tripoli +Africa/Tunis +Africa/Windhoek +America/Adak +America/Anchorage +America/Anguilla +America/Antigua +America/Araguaina +America/Aruba +America/Asuncion +America/Atka +America/Barbados +America/Belem +America/Belize +America/Boa_Vista +America/Bogota +America/Boise +America/Buenos_Aires +America/Cambridge_Bay +America/Cancun +America/Caracas +America/Catamarca +America/Cayenne +America/Cayman +America/Chicago +America/Chihuahua +America/Cordoba +America/Costa_Rica +America/Cuiaba +America/Curacao +America/Dawson +America/Dawson_Creek +America/Denver +America/Detroit +America/Dominica +America/Edmonton +America/Eirunepe +America/El_Salvador +America/Ensenada +America/Fort_Wayne +America/Fortaleza +America/Glace_Bay +America/Godthab +America/Goose_Bay +America/Grand_Turk +America/Grenada +America/Guadeloupe +America/Guatemala +America/Guayaquil +America/Guyana +America/Halifax +America/Havana +America/Hermosillo +America/Indiana/Indianapolis +America/Indiana/Knox +America/Indiana/Marengo +America/Indiana/Vevay +America/Indianapolis +America/Inuvik +America/Iqaluit +America/Jamaica +America/Jujuy +America/Juneau +America/Kentucky/Louisville +America/Kentucky/Monticello +America/Knox_IN +America/La_Paz +America/Lima +America/Los_Angeles +America/Louisville +America/Maceio +America/Managua +America/Manaus +America/Martinique +America/Mazatlan +America/Mendoza +America/Menominee +America/Merida +America/Mexico_City +America/Miquelon +America/Monterrey +America/Montevideo +America/Montreal +America/Montserrat +America/Nassau +America/New_York +America/Nipigon +America/Nome +America/Noronha +America/Panama +America/Pangnirtung +America/Paramaribo +America/Phoenix +America/Port-au-Prince +America/Port_of_Spain +America/Porto_Acre +America/Porto_Velho +America/Puerto_Rico +America/Rainy_River +America/Rankin_Inlet +America/Recife +America/Regina +America/Rio_Branco +America/Rosario +America/Santiago +America/Santo_Domingo +America/Sao_Paulo +America/Scoresbysund +America/Shiprock +America/St_Johns +America/St_Kitts +America/St_Lucia +America/St_Thomas +America/St_Vincent +America/Swift_Current +America/Tegucigalpa +America/Thule +America/Thunder_Bay +America/Tijuana +America/Tortola +America/Vancouver +America/Virgin +America/Whitehorse +America/Winnipeg +America/Yakutat +America/Yellowknife +Antarctica/Casey +Antarctica/Davis +Antarctica/DumontDUrville +Antarctica/Mawson +Antarctica/McMurdo +Antarctica/Palmer +Antarctica/South_Pole +Antarctica/Syowa +Antarctica/Vostok +Arctic/Longyearbyen +Asia/Aden +Asia/Almaty +Asia/Amman +Asia/Anadyr +Asia/Aqtau +Asia/Aqtobe +Asia/Ashgabat +Asia/Ashkhabad +Asia/Baghdad +Asia/Bahrain +Asia/Baku +Asia/Bangkok +Asia/Beirut +Asia/Bishkek +Asia/Brunei +Asia/Calcutta +Asia/Chungking +Asia/Colombo +Asia/Dacca +Asia/Damascus +Asia/Dhaka +Asia/Dili +Asia/Dubai +Asia/Dushanbe +Asia/Gaza +Asia/Harbin +Asia/Hong_Kong +Asia/Hovd +Asia/Irkutsk +Asia/Istanbul +Asia/Jakarta +Asia/Jayapura +Asia/Jerusalem +Asia/Kabul +Asia/Kamchatka +Asia/Karachi +Asia/Kashgar +Asia/Katmandu +Asia/Krasnoyarsk +Asia/Kuala_Lumpur +Asia/Kuching +Asia/Kuwait +Asia/Macao +Asia/Magadan +Asia/Manila +Asia/Muscat +Asia/Nicosia +Asia/Novosibirsk +Asia/Omsk +Asia/Phnom_Penh +Asia/Pyongyang +Asia/Qatar +Asia/Rangoon +Asia/Riyadh +Asia/Riyadh87 +Asia/Riyadh88 +Asia/Riyadh89 +Asia/Saigon +Asia/Samarkand +Asia/Seoul +Asia/Shanghai +Asia/Singapore +Asia/Taipei +Asia/Tashkent +Asia/Tbilisi +Asia/Tehran +Asia/Tel_Aviv +Asia/Thimbu +Asia/Thimphu +Asia/Tokyo +Asia/Ujung_Pandang +Asia/Ulaanbaatar +Asia/Ulan_Bator +Asia/Urumqi +Asia/Vientiane +Asia/Vladivostok +Asia/Yakutsk +Asia/Yekaterinburg +Asia/Yerevan +Atlantic/Azores +Atlantic/Bermuda +Atlantic/Canary +Atlantic/Cape_Verde +Atlantic/Faeroe +Atlantic/Jan_Mayen +Atlantic/Madeira +Atlantic/South_Georgia +Atlantic/Stanley +Australia/ACT +Australia/Adelaide +Australia/Brisbane +Australia/Broken_Hill +Australia/Canberra +Australia/Darwin +Australia/Hobart +Australia/LHI +Australia/Lindeman +Australia/Lord_Howe +Australia/Melbourne +Australia/NSW +Australia/North +Australia/Perth +Australia/Queensland +Australia/South +Australia/Sydney +Australia/Tasmania +Australia/Victoria +Australia/West +Australia/Yancowinna +Brazil/Acre +Brazil/DeNoronha +Brazil/East +Brazil/West +CET +CST6CDT +Canada/Atlantic +Canada/Central +Canada/East-Saskatchewan +Canada/Eastern +Canada/Mountain +Canada/Newfoundland +Canada/Pacific +Canada/Saskatchewan +Canada/Yukon +Chile/Continental +Chile/EasterIsland +Cuba +EET +EST +EST5EDT +Egypt +Eire +Etc/GMT+1 +Etc/GMT+10 +Etc/GMT+11 +Etc/GMT+12 +Etc/GMT+2 +Etc/GMT+3 +Etc/GMT+4 +Etc/GMT+5 +Etc/GMT+6 +Etc/GMT+7 +Etc/GMT+8 +Etc/GMT+9 +Etc/GMT-1 +Etc/GMT-10 +Etc/GMT-11 +Etc/GMT-12 +Etc/GMT-13 +Etc/GMT-14 +Etc/GMT-2 +Etc/GMT-3 +Etc/GMT-4 +Etc/GMT-5 +Etc/GMT-6 +Etc/GMT-7 +Etc/GMT-8 +Etc/GMT-9 +Europe/Amsterdam +Europe/Andorra +Europe/Athens +Europe/Belfast +Europe/Belgrade +Europe/Berlin +Europe/Bratislava +Europe/Brussels +Europe/Bucharest +Europe/Budapest +Europe/Chisinau +Europe/Copenhagen +Europe/Dublin +Europe/Gibraltar +Europe/Helsinki +Europe/Istanbul +Europe/Kaliningrad +Europe/Kiev +Europe/Lisbon +Europe/Ljubljana +Europe/London +Europe/Luxembourg +Europe/Madrid +Europe/Malta +Europe/Minsk +Europe/Monaco +Europe/Moscow +Europe/Nicosia +Europe/Oslo +Europe/Paris +Europe/Prague +Europe/Riga +Europe/Rome +Europe/Samara +Europe/San_Marino +Europe/Sarajevo +Europe/Simferopol +Europe/Skopje +Europe/Sofia +Europe/Stockholm +Europe/Tallinn +Europe/Tirane +Europe/Tiraspol +Europe/Uzhgorod +Europe/Vaduz +Europe/Vatican +Europe/Vienna +Europe/Vilnius +Europe/Warsaw +Europe/Zagreb +Europe/Zaporozhye +Europe/Zurich +GB +GB-Eire +GMT +HST +Hongkong +Indian/Antananarivo +Indian/Chagos +Indian/Christmas +Indian/Cocos +Indian/Comoro +Indian/Kerguelen +Indian/Mahe +Indian/Maldives +Indian/Mauritius +Indian/Mayotte +Indian/Reunion +Iran +Israel +Jamaica +Japan +Kwajalein +Libya +MET +MST +MST7MDT +Mexico/BajaNorte +Mexico/BajaSur +Mexico/General +Mideast/Riyadh87 +Mideast/Riyadh88 +Mideast/Riyadh89 +NZ +NZ-CHAT +Navajo +PRC +PST8PDT +Pacific/Apia +Pacific/Auckland +Pacific/Chatham +Pacific/Easter +Pacific/Efate +Pacific/Enderbury +Pacific/Fakaofo +Pacific/Fiji +Pacific/Funafuti +Pacific/Galapagos +Pacific/Gambier +Pacific/Guadalcanal +Pacific/Guam +Pacific/Honolulu +Pacific/Johnston +Pacific/Kiritimati +Pacific/Kosrae +Pacific/Kwajalein +Pacific/Majuro +Pacific/Marquesas +Pacific/Midway +Pacific/Nauru +Pacific/Niue +Pacific/Norfolk +Pacific/Noumea +Pacific/Pago_Pago +Pacific/Palau +Pacific/Pitcairn +Pacific/Ponape +Pacific/Port_Moresby +Pacific/Rarotonga +Pacific/Saipan +Pacific/Samoa +Pacific/Tahiti +Pacific/Tarawa +Pacific/Tongatapu +Pacific/Truk +Pacific/Wake +Pacific/Wallis +Pacific/Yap +Poland +Portugal +ROC +ROK +Singapore +SystemV/AST4 +SystemV/AST4ADT +SystemV/CST6 +SystemV/CST6CDT +SystemV/EST5 +SystemV/EST5EDT +SystemV/HST10 +SystemV/MST7 +SystemV/MST7MDT +SystemV/PST8 +SystemV/PST8PDT +SystemV/YST9 +SystemV/YST9YDT +Turkey +US/Alaska +US/Aleutian +US/Arizona +US/Central +US/East-Indiana +US/Eastern +US/Hawaii +US/Indiana-Starke +US/Michigan +US/Mountain +US/Pacific +US/Samoa +W-SU +WET + +</pre> + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/help/tl.html b/help/tl.html new file mode 100644 index 0000000000000000000000000000000000000000..621165414bbfbf4a52c7c2e5ea8982b2488b2822 --- /dev/null +++ b/help/tl.html @@ -0,0 +1,183 @@ +<HTML> +<HEAD><TITLE>Autopsy Timeline Analysis Help</TITLE></HEAD> +<BODY BGCOLOR=#CCCC99> + +<CENTER><H2>Timeline Mode</H2></CENTER> +<H3>Overview</H3> +<P> +For some investigations, creating a timeline of activity can be +useful to identify places where the analysis should begin. Of +course file times can be easily modified by an attacker, so they +can not be 100% trusted. But, Autopsy can create timelines of +file activity. + +<P> +Files have at least three times associated with them. The details of +each time varies with the file system type. + +<P> +The following times exist for UNIX file systems (EXT2FS & FFS): + +<UL> + <LI><B>Modified</B>: When the file data was last + modified. This time can be modified using the utimes() + function. This time is preserved in a 'tar' archive, so it is + possible to have M-times of files prior to when they were introduced + to the system. + + <LI><B>Accessed</B>: When the file data was last + accessed. This time can be modified using the utimes() function. + + <LI><B>Changed</B>: When the file status (inode data) + was last changed. This time can not be set using the utimes() + function in UNIX (but it will be set when utimes() is used to modify + other values). +</UL> + +The EXT2FS file system also has a Deleted time, but it is not displayed +in the timeline. + +<P> +A FAT File system has the following times: +<UL> + <LI><B>Written</B>: When the file was last written to. + It is the ONLY required time in the FAT file system. + + <LI><B>Accessed</B>: When the file was last accessed. In + FAT, it is only accurate to the day (not minute). It is an optional + value, so some Operating Systems may not update it. + + <LI><B>Created</B>: When the file was created. It is + also optional, so some Operating Systems may not update it. In fact, + many Windows installations have a C-Time of 0 for directories such as + <TT>C:\\Windows</TT> and <TT>C:\\Program Files</TT>. +</UL> + +<P> +The NTFS File system has several times, four of which are +used in the timeline. These times are gathered from the +<TT>\$STANDARD_INFORMATION</TT> attribute. +<UL> + <LI><B>Written</B>: When the file was last written to. + + <LI><B>Accessed</B>: When the file was last accessed. + + <LI><B>Changed</B>: When the MFT entry was last modified. + + <LI><B>Created</B>: When the file was created. +</UL> + + +<H3>How to Create a Timeline</H3> +Creating a timeline takes two steps. The first step extracts and +saves the needed data from each file system images. This step +stores the data from each specific file system in a generic format. +Historically (from TCT), this file was called the <TT>body</TT> +file. The second step takes the <TT>body</TT> file as input and +generates an ASCII timeline of file activity between two specified +dates. The resulting timeline can be viewed in Autopsy or using +a text editor. + + +<H3>Creating the Body File</H3> +The file meta-data must be extracted from the file system images and saved +to the <TT>body</TT> file. There are three major types of files that data +can be extracted for: +<UL> + <LI><B>Allocated Files</B>: +Files that are seen when doing an 'ls' or 'dir' in a directory. In +other words, these are the files that have an allocated file name +structure. + + <LI><B>Unallocated Files</B>: +Files that have been deleted, but that TSK can still access. +Files in this category include orphan files, which are files that +no longer have a name, but whose metadata still exists. +If a deleted file name points to an allocated metadata structure, +then the name will say (realloc) next to it. + +</UL> + +<P> +To create the <TT>body</TT> file, select the images to analyze from +the list on top. Next, select which types of data that you want to +extract. By default all types are extracted. Lastly, identify the +name of the body file to create. The file will be created in the +<TT>output</TT> directory and an entry will be added to the host config +file. You will be given the option to calculate the MD5 value of +the new file. + + +<H3>Creating the Timeline</H3> +The next window allows one to create a timeline based on the newly +created <TT>body</TT> file. Or, one can select the option from +the left-hand side menu. The range of dates must be selected as +well as the name of the timeline file. The resulting timeline will +use the time zone for the host. + +<P> +If the images are from a +UNIX file system, then the password and group files can be used to +change the UID and GID to actual names. If the partition from the +root directory exists in the host, select it from the pull down +list and Autopsy will find the <TT>/etc/passwd</TT> and +<TT>/etc/group</TT> file contents. + +<P> +The timeline will be created in the <TT>output</TT> directory. +You will be given the option to calculate the MD5 hash value of +the new file. + +<H3>Viewing the Timeline</H3> +The timeline can be viewed in Autopsy. Timelines tend to be very +large though and have thousands of lines. HTML browsers can not +handle tables of this size very well and typically have trouble +processing it. Therefore, Autopsy only allows you to view the +timeline one month at a time. It will likely be easier to open a +shell and examine the timeline in a text editor or pager such as +'less' or 'more'. + +<P> +The 'summary' link will show a page that contains a monthly summary +of activity. It shows how many many events occured in that month +and links to the details. This allows one to get a high level +view of when a lot of activity last occured. + +<P> +The following columns are in the timeline (in order): +<UL> + <LI><B>Date and time</B>of the activity. If no date is given, + then the activity occured at the same time as the previous entry + with a time. + + <LI><B>Size</B>. The size of the file. + + <LI><B>Entry Type</B>. The 'm', 'a', 'c', and 'b' letters will exist to + identify which of the activity types this entry corresponds to. 'm' is + for modified times, 'a' is for access times, 'c' is for change times, and + 'b' is for created (or born) times. + + <LI><B>Mode</B. The UNIX mode is shown. + + <LI><B>UID</B>. The User Id or User name is shown. If a password + file was provided when the timeline was created, then the colunn should + only have names. + + <LI><B>GID</B>. The Group Id or Group name is shown. If a group + file was provided when the timeline was created, then the colunn should + only have names. + + <LI><B>Meta Data Address</B>. The inode or MFT entry address for the + associated file. + + <LI><B>File Name</B>. The name of the file and the destination of a + symbolic link. Deleted entries will have '(deleted)' at the end and + deleted entries that point to an allocated meta data structure will + have '(realloc)'. + + +</UL> + +<HR> +<FONT SIZE=0>Brian Carrier</FONT> +</BODY></HTML> diff --git a/lib/.perltidyrc b/lib/.perltidyrc new file mode 100755 index 0000000000000000000000000000000000000000..f1f2e71a01bcfcdb8bd171c25e4d7a1e1e797c93 --- /dev/null +++ b/lib/.perltidyrc @@ -0,0 +1,5 @@ +-i=4 # indent of 4 +-pt=2 # paren tightness +-sbt=2 # square paren tightness +-bt=2 # curly paren tightness +-nsfs # no space after semi in for loop diff --git a/lib/Appsort.pm b/lib/Appsort.pm new file mode 100644 index 0000000000000000000000000000000000000000..bdf82bfad6e334f104a1b96bc93e50180f5cf451 --- /dev/null +++ b/lib/Appsort.pm @@ -0,0 +1,399 @@ +# +# Sort files based on their application type (content) +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2008 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Appsort; + +$Appsort::FRAME = 1; +$Appsort::MENU = 2; +$Appsort::ENTER = 3; +$Appsort::RUN = 4; +$Appsort::VIEW = 5; +$Appsort::BLANK = 6; + +sub main { + + if ($::LIVE == 1) { + Print::print_html_header("Unsupported for Live Analysis"); + print +"<center><h2>This feature is not available during a live analysis</h2></center>"; + Print::print_html_footer(); + return 0; + } + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Appsort::FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view == $Appsort::BLANK) { + return blank(); + } + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the meta data address + if ($view == $Appsort::FRAME) { + return frame(); + } + elsif ($view == $Appsort::ENTER) { + return enter(); + } + elsif ($view == $Appsort::MENU) { + return menu(); + } + elsif ($view == $Appsort::RUN) { + return run(); + } + elsif ($view == $Appsort::VIEW) { + return view(); + } + else { + Print::print_check_err("Invalid Application Sorting View"); + } +} + +sub get_sorter_dir { + if ($Args::args{'vol'} =~ /^($::REG_VNAME)$/) { + return "$::host_dir" . "$::DATADIR/sorter-$1/"; + } + Print::print_err("Invalid Sorter Directory"); +} + +sub get_sorter_graphics_dir { + if ($Args::args{'vol'} =~ /^($::REG_VNAME)$/) { + return "$::host_dir" . "$::DATADIR/sorter-graphics-$1/"; + } + Print::print_err("Invalid Sorter Graphics Directory"); +} + +# sorter frameset +sub frame { + Print::print_html_header_frameset("Sorter on $Args::args{'vol'}"); + + print "<frameset cols=\"20%,80%\">\n"; + + # Block List + print "<frame src=\"$::PROGNAME?mod=$::MOD_APPSORT&view=$Appsort::MENU&" + . "$Args::baseargs\">\n"; + + # Blank + print "<frame src=\"$::PROGNAME?mod=$::MOD_APPSORT&view=$Appsort::BLANK&" + . "$Args::baseargs\" name=\"content\">\n" + . "</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# The left-hand frame for running sorter +sub menu { + Print::print_html_header("sorter menu"); + + print "<p><a href=\"$::PROGNAME?mod=$::MOD_APPSORT&view=$Appsort::ENTER&" + . "$Args::baseargs\" " + . "target=\"content\">Sort Files by Type</a>"; + + print "<p><a href=\"$::PROGNAME?mod=$::MOD_APPSORT&view=$Appsort::VIEW&" + . "$Args::baseargs\" " + . "target=\"content\">View Sorted Files</a>"; + + Print::print_html_footer(); + return 0; +} + +# Get the data and print the form so that sorter can be run +sub enter { + Print::print_html_header("sorter - enter data to create"); + + print "<center>" + . "<h3>File Type Sortings</h3></center><br>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_APPSORT\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Appsort::RUN\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Args::args{'vol'}\">\n" + . Args::make_hidden(); + + print <<EOF1; +<p>The <b>sorter</b> tool will process an image and organize the +files based on their file type. The files are organized into categories +that are defined in configuration files. The categories will be saved +in the <tt>$::DATADIR</tt> directory. +<hr> +EOF1 + + my $sort_dir = get_sorter_dir(); + if (-d "$sort_dir") { + print "WARNING: This will overwrite any existing data in:<br>" + . " <tt>$sort_dir</tt><br>\n"; + } + + my $tab = " "; + + print <<EOF2; + +<p> +<input type=\"checkbox\" name=\"sorter_cat\" value=\"1\" CHECKED> +Sort files into categories by type + + <p>$tab + <input type=\"checkbox\" name=\"sorter_unk\" value=\"1\"> + Do not save data about <tt>unknown</tt> file types + + <p>$tab + <input type=\"checkbox\" name=\"sorter_save\" value=\"1\"> + Save a copy of files in category directory (may require lots of disk space) + + <p>$tab + <input type=\"checkbox\" name=\"sorter_img\" value=\"1\"> + Save ONLY graphic images and make thumbnails <br> + $tab (may require lots of disk space and will save to a different directory than sorting all file types) + +<p> +<input type=\"checkbox\" name=\"sorter_ext\" value=\"1\" CHECKED> +Extension and File Type Validation + +EOF2 + + if (($::NSRLDB ne "") && (-e "$::NSRLDB")) { + + # NSRL + print +"<p><input type=\"checkbox\" name=\"sorter_nsrl\" value=\"1\" CHECKED>" + . "Exclude files in the <b>NIST NSRL</b>\n"; + } + + if (($Caseman::alert_db ne "") && (-e "$Caseman::alert_db")) { + print +"<p><input type=\"checkbox\" name=\"sorter_alert\" value=\"1\" CHECKED>" + . "Alert files that are found in the <b>Alert Hash Database</b>\n"; + } + + if (($Caseman::exclude_db ne "") && (-e "$Caseman::exclude_db")) { + print +"<p><input type=\"checkbox\" name=\"sorter_exclude\" value=\"1\" CHECKED>" + . "Ignore files that are found in the <b>Exclude Hash Database</b>\n"; + } + + print "<p><input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return; +} + +# Run sorter on the image +sub run { + Print::print_html_header("sorter - create"); + + my $sort_args = ""; + my $ext = 0; + my $cat = 0; + + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + Print::log_host_inv("Running 'sorter' on ($Caseman::vol2sname{$vol}"); + + $ext = 1 + if ( (exists $Args::args{'sorter_ext'}) + && ($Args::args{'sorter_ext'} == 1)); + $cat = 1 + if ( (exists $Args::args{'sorter_cat'}) + && ($Args::args{'sorter_cat'} == 1)); + + if (($cat == 0) && ($ext == 0)) { + print "At least one action must be selected\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_APPSORT&" + . "view=$Appsort::ENTER&$Args::baseargs\">" + . "<img border=0 src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=43 height=20></a>\n"; + + return; + } + + # If both actions are wanted then no flags are needed + $sort_args .= "-e " if (($ext == 1) && ($cat == 0)); + $sort_args .= "-E " if (($ext == 0) && ($cat == 1)); + + my $sort_dir = get_sorter_dir(); + + if ($cat == 1) { + if ( (exists $Args::args{'sorter_img'}) + && ($Args::args{'sorter_img'} == 1)) + { + my $config = "$::TSKDIR/../share/tsk3/sorter/images.sort"; + + Print::print_err("images configuration file not found ($config)") + unless (-e "$config"); + + $sort_args .= "-C \'$config\' -s -U "; + + $sort_dir = get_sorter_graphics_dir(); + + } + else { + $sort_args .= "-s " + if ( (exists $Args::args{'sorter_save'}) + && ($Args::args{'sorter_save'} == 1)); + + $sort_args .= "-U " + if ( (exists $Args::args{'sorter_unk'}) + && ($Args::args{'sorter_unk'} == 1)); + } + } + + if ($::NSRLDB ne "") { + $sort_args .= "-n \'$::NSRLDB\' " + if ( (exists $Args::args{'sorter_nsrl'}) + && ($Args::args{'sorter_nsrl'} == 1)); + } + + if ($Caseman::alert_db ne "") { + $sort_args .= "-a \'$Caseman::alert_db\' " + if ( (exists $Args::args{'sorter_alert'}) + && ($Args::args{'sorter_alert'} == 1)); + } + + if ($Caseman::exclude_db ne "") { + $sort_args .= "-x \'$Caseman::exclude_db\' " + if ( (exists $Args::args{'sorter_exclude'}) + && ($Args::args{'sorter_exclude'} == 1)); + } + + unless (-d "$sort_dir") { + unless (mkdir "$sort_dir", $::MKDIR_MASK) { + Print::print_err("Error making $sort_dir"); + } + } + if (-e "$sort_dir/index.html") { + unlink("$sort_dir/index.html"); + } + + my $exec = +"-h -m '$mnt' -d '$sort_dir' -o $offset -i $imgtype -f $ftype $sort_args $img"; + + # print "Executing: <tt>sorter $exec</tt><p>\n"; + + # Execute Sorter + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + alarm(5); + + local *OUT; + Exec::exec_pipe(*OUT, "LANG=C LC_ALL=C '$::TSKDIR/sorter' $exec"); + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + + while ($_ = Exec::read_pipe_line(*OUT)) { + print "$_<br>\n"; + $hit_cnt = 0; + } + + close(OUT); + + if (-e "$sort_dir/index.html") { + print "<p>Output can be found by viewing:<br>" + . " <tt>$sort_dir/index.html</tt><p>\n"; + + # Print the index.html file from the output + print "<hr><center><h3>Results Summary</h3></center>\n"; + open INDEX, "<$sort_dir/index.html" + or die "Can't open sorter index file ($sort_dir/index.html)"; + + while (<INDEX>) { + next if ((/^<HTML><HEAD><TITLE>/i) + || (/^<BODY><center><H2>/i)); + + # Extract out the symlinks to the categories + if (/^\s*<li><a href="\.\/[\w\.]+">([\w\s]+)<\/a> \((\d+)\)\s*$/i) { + print "<LI>$1 ($2)\n"; + } + + # Skip the link on the thumbnails link + elsif (/^\s*\(<a href=[\"\.\/\w]+>thumbnails<\/A>\)\s*$/) { + print "(thumbnails)\n"; + } + else { + print "$_"; + } + } + close(INDEX); + } + + Print::print_html_footer(); + return; +} + +# View Page +sub view { + Print::print_html_header(""); + print "<center><h3>File Type Sorting</h3>\n" + . "Autopsy does not currently support viewing the sorted files.<br>\n" + . "After sorting, you can view the results by opening the following file:<p>\n"; + print "<tt>" . get_sorter_dir() . "index.html</tt>"; + + Print::print_html_footer(); + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header(""); + print "<center><h3>File Type Sorting</h3>\n" + . "In this mode, Autopsy will examine allocated and unallocated files<br> and " + . "sort them into categories and verify the extension.<p>This allows you to find a file based on" + . "its type and find \"hidden\" files.<p>\n" + . + + "WARNING: This can be a time intensive process.<br>\n"; + + Print::print_html_footer(); + return 0; +} + diff --git a/lib/Appview.pm b/lib/Appview.pm new file mode 100644 index 0000000000000000000000000000000000000000..3a407fa2e1a081471ff5286534c3cef7e6d6ac9d --- /dev/null +++ b/lib/Appview.pm @@ -0,0 +1,291 @@ +# +# View the application layer (HTML, picutures etc.) +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Updated 1/15 + +package Appview; + +$Appview::CELL_FRAME = 1; +$Appview::CELL_MENU = 2; +$Appview::CELL_CONT = 3; + +sub main { + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Appview::CELL_FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + # Check Basic Args + Args::check_vol('vol'); + Args::check_meta('meta'); + Args::check_dir(); + Args::check_recmode(); + + if ($view == $Appview::CELL_FRAME) { + return cell_frame(); + } + elsif ($view == $Appview::CELL_CONT) { + return cell_content(); + } + elsif ($view == $Appview::CELL_MENU) { + return cell_menu(); + } + else { + Print::print_check_err("Invalid Application Viewing View"); + } +} + +######################################################################### +# +# CELL - Sanitized Environment +# + +my $CELL_MODE_SANIT = 1; +my $CELL_MODE_NORM = 2; + +sub cell_frame { + Print::print_html_header_frameset("Autopsy Cell"); + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $fname = "$mnt$Args::args{'dir'}"; + + print "<frameset rows=\"15%,85%\">\n"; + + # if a mode was not given, then choose the Sanitized by default + $Args::args{'cell_mode'} = $CELL_MODE_SANIT + unless ((exists $Args::args{'cell_mode'}) + && ($Args::args{'cell_mode'} =~ /^\d$/)); + + my $url = + "&$Args::baseargs&meta=$Args::enc_args{'meta'}" + . "&dir=$Args::enc_args{'dir'}&" + . "cell_mode=$Args::args{'cell_mode'}&recmode=$Args::args{'recmode'}"; + + print +"<frame src=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_MENU${url}\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_CONT${url}\">\n" + . "</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# Print the menu on top. This allows one to export the file and change modes +sub cell_menu { + Args::check_cell_mode(); + + Print::print_html_header("Cell Header"); + + my $cell_mode = $Args::args{'cell_mode'}; + + my $url = + "&$Args::baseargs&meta=$Args::enc_args{'meta'}&" + . "dir=$Args::enc_args{'dir'}&recmode=$Args::enc_args{'recmode'}"; + + if ($cell_mode == $CELL_MODE_SANIT) { + + print <<EOF1; +<center> +This file is currently being viewed in a <b>sanitized environment</b><br> +HTML files have been edited to disable scripts and links. +The script contents will be shown as text.<br> +Pictures have been replaced by place holders<br> + +<table width=300 cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center> + <a href=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_FRAME$url&cell_mode=$CELL_MODE_NORM\" + target=\"_top\"> + <img src=\"pict/sanit_b_norm.jpg\" alt=\"Normal\" border=\"0\"> + </a> + </td> +EOF1 + + } + + elsif ($cell_mode == $CELL_MODE_NORM) { + print <<EOF2; +<center> +This file is currently being viewed in a <b>normal environment</b><br> +HTML files are being viewed without modification.<br> + +<table width=300 cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center> + <a href=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_FRAME&$url&cell_mode=$CELL_MODE_SANIT\" + target=\"_top\"> + <img src=\"pict/sanit_b_san.jpg\" alt=\"Sanitized\" border=\"0\"> + </a> + </td> +EOF2 + } + + # Export the file + print "<td align=center>\n" + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::EXPORT&$url\">" + . "<img src=\"pict/but_export.jpg\" alt=\"export\" border=\"0\" " + . "width=123 height=20>" + . "</a></td></tr>\n"; + + print "<tr><td colspan=\"2\" align=\"center\">" + . "Deleted File Recovery Mode</td></tr>\n" + if ($Args::enc_args{'recmode'} == $File::REC_YES); + + print "</table>"; + + Print::print_html_footer(); + return; +} + +# Display safe and common things in the browser (pictures, basic html) +sub cell_content { + Args::check_meta('meta'); + Args::check_dir(); + Args::check_cell_mode(); + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $fname = "$mnt$Args::args{'dir'}"; + + my $recflag = ""; + + $recflag = " -r " + if (Args::get_recmode() == $File::REC_YES); + + # identify what type it is + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + my $file_type = Exec::read_pipe_line(*OUT); + close(OUT); + + $file_type = "Error getting file type" + if ((!defined $file_type) || ($file_type eq "")); + + if ($file_type =~ /JPEG image data/) { + Print::log_host_inv("$vol: Viewing $fname ($meta) as JPEG"); + print "Content-type: image/jpeg$::HTTP_NL$::HTTP_NL"; + } + elsif ($file_type =~ /GIF image data/) { + Print::log_host_inv("$vol: Viewing $fname ($meta) as GIF"); + print "Content-type: image/gif$::HTTP_NL$::HTTP_NL"; + } + elsif ($file_type =~ /PNG image data/) { + Print::log_host_inv("$vol: Viewing $fname ($meta) as PNG"); + print "Content-type: image/png$::HTTP_NL$::HTTP_NL"; + } + elsif ($file_type =~ /PC bitmap data/) { + Print::log_host_inv("$vol: Viewing $fname ($meta) as BMP"); + print "Content-type: image/bmp$::HTTP_NL$::HTTP_NL"; + } + elsif ($file_type =~ /HTML document text/) { + Print::log_host_inv("$vol: Viewing $fname ($meta) as HTML"); + print "Content-type: text/html$::HTTP_NL$::HTTP_NL"; + } + else { + Print::log_host_inv("$vol: Unknown format of meta $meta "); + Print::print_check_err("Unknown File Type for Viewing: $file_type"); + } + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + + while ($_ = Exec::read_pipe_line(*OUT)) { + + # Parse out bad "stuff" + if ( ($file_type =~ /HTML document text/) + && ($Args::args{'cell_mode'} == $CELL_MODE_SANIT)) + { + $_ =~ s/\bsrc=/src=$::SANITIZE_TAG\?/ig; + $_ =~ s/\bhref=/href=$::SANITIZE_TAG\?/ig; + $_ =~ s/<script/<$::SANITIZE_TAG-script/ig; + $_ =~ s/\bbackground=/background=$::SANITIZE_TAG\?/ig; + } + print "$_"; + } + print "$::HTTP_NL$::HTTP_NL"; + close(OUT); + return 0; +} + +sub sanitize_pict { + my $url = shift(); + my $lurl = $url; + $lurl =~ tr/[A-Z]/[a-z]/; + + print "HTTP/1.0 200 OK$::HTTP_NL"; + if ( ($lurl =~ /.jpg/i) + || ($lurl =~ /.jpeg/i) + || ($lurl =~ /.gif/i) + || ($lurl =~ /.png/i) + || ($lurl =~ /.bmp/i)) + { + + open PICT, "<$::PICTDIR/$::SANITIZE_PICT" + or die "can not open $::PICTDIR/$::SANITIZE_PICT"; + + print "Content-type: image/jpeg$::HTTP_NL$::HTTP_NL"; + while (<PICT>) { + print "$_"; + } + close(PICT); + print "$::HTTP_NL$::HTTP_NL"; + } + else { + $url =~ tr/\+/ /; + $url =~ s/%([a-f0-9][a-f0-9])/chr( hex( $1 ) )/eig; + + Print::print_html_header("Denied"); + print "<h1><center>Unable to Complete Request</h1><br>\n" + . "<tt>Autopsy</tt> will not follow links from " + . "untrusted HTML pages:<br><tt>$url</tt><br>\n"; + Print::print_html_footer(); + } + + exit(0); +} + diff --git a/lib/Args.pm b/lib/Args.pm new file mode 100644 index 0000000000000000000000000000000000000000..705cc89a39a2a67d32cae6da4bbce8660656b718 --- /dev/null +++ b/lib/Args.pm @@ -0,0 +1,927 @@ +# +# Functions to check and get the arguments from URL +# +# ver 2.00+ +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2003-2004 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Args; + +# Parse the argument string into the args hash +sub parse_args { + my $lcl_args = shift; + foreach my $nam_val (split(/&/, $lcl_args)) { + my ($name, $value) = split(/=/, $nam_val); + if (defined $value) { + my $dec_name = url_decode($name); + $Args::enc_args{$dec_name} = $value; + $Args::args{$dec_name} = url_decode($value); + } + } +} + +sub url_encode { + my $text = shift; + $text =~ s/([^a-z0-9_.!~*'() -])/sprintf "%%%02X", ord($1)/eig; + $text =~ tr/ /+/; + return $text; +} + +sub url_decode { + my $text = shift; + $text =~ tr/\+/ /; + $text =~ s/%([a-f0-9][a-f0-9])/chr( hex( $1 ) )/eig; + return $text; +} + +# This assumes that the checking of the types has been done and this just +# makes a string of the key values if they exist +# +# case +# host +# img + +# Must add & after +sub make_baseargs { + $Args::baseargs = ""; + + # The standard case, host, and investigator + $Args::baseargs .= "case=$Args::enc_args{'case'}&" + if ((exists $Args::enc_args{'case'}) && ($Args::enc_args{'case'} ne "")); + $Args::baseargs .= "host=$Args::enc_args{'host'}&" + if ((exists $Args::enc_args{'host'}) && ($Args::enc_args{'host'} ne "")); + $Args::baseargs .= "inv=$Args::enc_args{'inv'}&" + if ((exists $Args::enc_args{'inv'}) && ($Args::enc_args{'inv'} ne "")); + + $Args::baseargs_novol = $Args::baseargs; + + # Add the image, file system type, and mount point + $Args::baseargs .= "vol=$Args::enc_args{'vol'}&" + if ((exists $Args::enc_args{'vol'}) && ($Args::enc_args{'vol'} ne "")); + + # remove the final '&' + $Args::baseargs_novol = $1 if ($Args::baseargs_novol =~ /^(.*?)&$/); + $Args::baseargs = $1 if ($Args::baseargs =~ /^(.*?)&$/); + + return; +} + +# Does not do mnt or img +sub make_hidden { + my $str = ""; + + $str .= + "<input type=\"hidden\" name=\"host\" value=\"$Args::args{'host'}\">\n" + if ((exists $Args::args{'host'}) && ($Args::args{'host'} ne "")); + + $str .= + "<input type=\"hidden\" name=\"case\" value=\"$Args::args{'case'}\">\n" + if ((exists $Args::args{'case'}) && ($Args::args{'case'} ne "")); + + $str .= + "<input type=\"hidden\" name=\"inv\" value=\"$Args::args{'inv'}\">\n" + if ((exists $Args::args{'inv'}) && ($Args::args{'inv'} ne "")); + + return $str; +} + +############################### +# block +############################### +sub check_block { + if ((!exists $Args::args{'block'}) || ($Args::args{'block'} !~ /^\d+$/)) { + Print::print_check_err( + "Invalid block argument (positive numbers only)"); + } + return 0; +} + +sub get_block { + if ($Args::args{'block'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid Block"); +} + +############################### +# body +############################### + +sub check_body { + unless (exists $Args::args{'body'}) { + Print::print_check_err("Missing body argument"); + } + unless ($Args::args{'body'} =~ /^$::REG_VNAME$/o) { + Print::print_check_err( + "Invalid body value (only letters, " . "numbers,-,., and _)"); + } + return 0; +} + +sub get_body { + if ($Args::args{'body'} =~ /^($::REG_VNAME)$/o) { + return $1; + } + Print::print_err("Invalid Body"); +} + +################################ +# Case name +################################ + +sub check_case { + unless (exists $Args::args{'case'}) { + Print::print_check_err("Missing case argument"); + } + unless ($Args::args{'case'} =~ /^$::REG_CASE$/o) { + Print::print_check_err( + "Invalid case value (letters, num, and symbols only"); + } + return 0; +} + +sub get_case { + if ($Args::args{'case'} =~ /^($::REG_CASE)$/o) { + return $1; + } + Print::print_err("Invalid Case Name"); +} + +############################### +# cell_mode +############################### +sub check_cell_mode { + if ( (!exists $Args::args{'cell_mode'}) + || ($Args::args{'cell_mode'} !~ /^\d$/o)) + { + Print::print_check_err( + "Invalid cell_mode argument (numbers >= 0 only)"); + } + return 0; +} + +################################ +# dir +################################ +sub check_dir { + if ( (!exists $Args::args{'dir'}) + || ($Args::args{'dir'} =~ /\/\.\.\//) + || ($Args::args{'dir'} =~ /\;/)) + { + Print::print_check_err("Invalid dir argument (valid file path only)"); + } + return 0; +} + +sub get_dir { + if ($Args::args{'dir'} =~ /([^;]*)/o) { + my $d = $1; + + # Remove double slashes + $d =~ s/\/\//\//g; + return $d; + } + Print::print_err("Invalid Directory"); +} + +############################### +# dirmode +############################### +sub check_dirmode { + if ((!exists $Args::args{'dirmode'}) || ($Args::args{'dirmode'} !~ /^\d+$/)) + { + Print::print_check_err( + "Invalid dirmode argument (positive numbers only)"); + } + return 0; +} + +sub get_dirmode { + if ($Args::args{'dirmode'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid dirmode"); +} + +################################ +# do_md5 +################################ +sub check_do_md5 { + if ((!exists $Args::args{'do_md5'}) || ($Args::args{'do_md5'} !~ /^\d+$/)) { + Print::print_check_err("Missing do_md5 argument"); + } + return 0; +} + +sub get_do_md5 { + if ($Args::args{'do_md5'} =~ /^\s*(\d+)$/) { + return $1; + } + Print::print_err("Invalid MD5 Flag"); +} + +################################ +# fname +################################ + +sub check_fname { + unless (exists $Args::args{'fname'}) { + Print::print_check_err("Missing fname argument"); + } + unless ($Args::args{'fname'} =~ /^$::REG_FNAME$/o) { + Print::print_check_err( + "Invalid fname value (only letters, " . "numbers,-,., and _)"); + } + return 0; +} + +sub get_fname { + if ($Args::args{'fname'} =~ /^($::REG_FNAME)$/o) { + return "$::host_dir" . "$::DATADIR/$1"; + } + Print::print_err("Invalid File Name"); +} + +################################ +# fname_mode +################################ +sub check_fname_mode { + if (!exists $Args::args{'fname_mode'}) { + Print::print_check_err("Missing fname_mode argument"); + } + unless ($Args::args{'fname_mode'} =~ /^\d+$/) { + Print::print_check_err("invalid mode: numbers only"); + } + return 0; +} + +################################ +# fname_rel +# Return the relative fname +################################ +sub get_fname_rel { + if ($Args::args{'fname'} =~ /^($::REG_FNAME)$/o) { + return "$::DATADIR/$1"; + } + Print::print_err("Invalid Relative File Name"); +} + +############################### +# force +############################### +sub get_force { + if ($Args::args{'force'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid Force Flag"); +} + +################################ +# ftype +################################ +sub get_ftype_blah { + if (exists $Args::args{'ftype'}) { + if ($Args::args{'ftype'} =~ /^($::REG_FTYPE)$/o) { + return $1; + } + } + if ( (exists $Args::args{'img'}) + && (exists $Caseman::vol2ftype{$Args::args{'img'}})) + { + return $Caseman::vol2ftype{$Args::args{'img'}}; + } + Print::print_err("Missing ftype value"); +} + +sub check_ftype_blah { + unless ( + ( + (exists $Args::args{'img'}) + && (exists $Caseman::vol2ftype{$Args::args{'img'}}) + && ($Caseman::vol2ftype{$Args::args{'img'}} =~ /^$::REG_FTYPE$/o) + ) + || ( (exists $Args::args{'ftype'}) + && ($Args::args{'ftype'} =~ /^$::REG_FTYPE$/o)) + ) + { + Print::print_check_err("Missing or invalid ftype value"); + } + return 0; +} + +################################ +# host +# Host for the case +################################ + +sub check_host { + unless (exists $Args::args{'host'}) { + Print::print_check_err("Missing host argument"); + } + unless ($Args::args{'host'} =~ /^$::REG_HOST$/o) { + Print::print_check_err("Invalid host value"); + } + return 0; +} + +sub get_host { + if ($Args::args{'host'} =~ /^($::REG_HOST)$/o) { + return $1; + } + Print::print_err("Invalid Host"); +} + +################################ +# htype +################################ +sub check_htype { + if ((!exists $Args::args{'htype'}) || ($Args::args{'htype'} !~ /^\d+$/)) { + Print::print_check_err( + "Invalid htype argument (positive numbers only)"); + } + return 0; +} + +sub get_htype { + if ($Args::args{'htype'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid htype"); +} + +############################### +# ifind +# ifind is optional and by default is 0 if not given +############################### +sub get_ifind { + if (!exists $Args::args{'ifind'}) { + return 0; + } + elsif ($Args::args{'ifind'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid ifind flag"); +} + +############################### +# img_path is used when adding images - it is the full path to the +# non-evidence locker copy of the image +############################### + +sub check_img_path { + if (!exists $Args::args{'img_path'}) { + Print::print_check_err("Missing image (img_path) argument"); + } + elsif ($Args::args{'img_path'} =~ /^$::REG_IMG_PATH$/o) { + + # Check for its actual existence + + Print::print_check_err("Image not found at $Args::args{'img_path'}") + unless ( + (-e "$Args::args{'img_path'}") + || ( (-l "$Args::args{'img_path'}") + && (-e readlink "$::host_dir" . "$Args::args{$img}")) + ); + } + else { + Print::print_check_err("Invalid image path (only letters, " + . "numbers,-,.,_/ and start with /) [$Args::args{'img_path'}]"); + } + + return 0; +} + +sub get_img_path { + if ($Args::args{'img_path'} =~ /^($::REG_IMG_PATH)$/o) { + return "$1"; + } + Print::print_err("Invalid Image Path"); +} + +sub check_img_path_wild { + if (!exists $Args::args{'img_path'}) { + Print::print_check_err("Missing wild image (img_path) argument"); + } + elsif ($Args::args{'img_path'} !~ /^$::REG_IMG_PATH_WILD$/o) { + + # IF there is extra white space then remove it and move on + if ($Args::args{'img_path'} =~ /^\s*($::REG_IMG_PATH_WILD)\s*$/o) { + $Args::args{'img_path'} = $1; + return 0; + } + else { + Print::print_check_err("Invalid wild image (img_path) argument"); + } + } + + return 0; +} + +sub get_img_path_wild { + if ($Args::args{'img_path'} =~ /^($::REG_IMG_PATH_WILD)$/o) { + return "$1"; + } + Print::print_err("Invalid Image Path"); +} + +############################### +# meta +############################### + +sub check_meta { + my $meta = shift; + if ( (!exists $Args::args{$meta}) + || ($Args::args{$meta} !~ /^$::REG_META$/o)) + { + Print::print_check_err( + "Invalid meta address ($meta) argument (numbers >= 0 only)"); + } + return 0; +} + +sub get_meta { + my $meta = shift; + if ($Args::args{$meta} =~ /^($::REG_META)$/o) { + return $1; + } + Print::print_err("Invalid Meta Address"); +} + +################################ +# inv +# Investigator +################################ + +sub check_inv { + unless (exists $Args::args{'inv'}) { + Print::print_check_err("Missing inv argument"); + } + unless ($Args::args{'inv'} =~ /^$::REG_INVESTIG$/o) { + Print::print_check_err( + "Invalid inv value (letters, num, and symbols only"); + } + return 0; +} + +sub get_inv { + if ($Args::args{'inv'} =~ /^($::REG_INVESTIG)$/o) { + return $1; + } + Print::print_err("Invalid Investigator"); +} + +############################### +# len +############################### +sub check_len { + if ( (!exists $Args::args{'len'}) + || ($Args::args{'len'} !~ /^\d+$/) + || ($Args::args{'len'} == 0)) + { + Print::print_check_err("Invalid len argument (positive numbers only)"); + } + return 0; +} + +sub get_len { + if ((exists $Args::args{'len'}) && ($Args::args{'len'} =~ /^(\d+)$/)) { + return $1; + } + + # return the default len of 1 if it is not defined + return 1; +} + +############################### +# min +############################### +sub get_min { + if ($Args::args{'min'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid Minute"); +} + +################################ +# module +################################ +sub check_mod { + if ((!exists $Args::args{'mod'}) || ($Args::args{'mod'} !~ /^\d+$/)) { + Print::print_check_err( + "Invalid Module argument (positive numbers only)"); + } + return 0; +} + +sub get_mod { + if ($Args::args{'mod'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid Module"); +} + +################################ +# mnt +############################### + +sub check_mnt { + my $ftype = Args::get_ftype(); + if (($ftype eq "blkls") || ($ftype eq "swap") || ($ftype eq "raw")) { + $Args::args{'mnt'} = $ftype; + $Args::enc_args{'mnt'} = $ftype; + } + elsif (!exists $Args::args{'mnt'}) { + + # Look it up if it is not found + if (exists $Args::args{'img'}) { + unless (exists $Caseman::vol2mnt{$Args::args{'img'}}) { + Print::print_check_err( + "Mounting point not found: $Args::args{'img'}"); + } + my $mnt = $Caseman::vol2mnt{$Args::args{'img'}}; + $Args::args{'mnt'} = $mnt; + $Args::enc_args{'mnt'} = Args::url_encode($mnt); + } + else { + Print::print_check_err("Mounting point not found"); + } + } + if ($Args::args{'mnt'} =~ /\/\.\.\//) { + Print::print_check_err( + "Invalid mount point argument (valid file path only)"); + } + unless ($Args::args{'mnt'} =~ /^$::REG_MNT$/o) { + Print::print_check_err( + "Invalid mount point argument (valid file path only)"); + } + return 0; +} + +sub get_mnt { + if ((exists $Args::args{'mnt'}) && ($Args::args{'mnt'} =~ /($::REG_MNT)/o)) + { + return $1; + } + Print::print_err("Invalid Mounting Point"); +} + +################################ +# note +################################ +sub check_note { + if (!exists $Args::args{'note'}) { + Print::print_check_err("Missing note argument"); + } + return 0; +} + +################# +# num_img - adding disk images + +sub check_num_img { + if ((!exists $Args::args{'num_img'}) || ($Args::args{'num_img'} !~ /^\d+$/)) + { + Print::print_check_err( + "Invalid num_img argument (positive numbers only)"); + } + return 0; +} + +sub get_num_img { + if ($Args::args{'num_img'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid num_img"); +} + +############################### +# recmode +############################### +sub check_recmode { + if ((!exists $Args::args{'recmode'}) || ($Args::args{'recmode'} !~ /^\d+$/)) + { + Print::print_check_err( + "Invalid recmode argument (positive numbers only)"); + } + return 0; +} + +sub get_recmode { + if ($Args::args{'recmode'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid recmode"); +} + +################################ +# srchidx +# +# Index for previous keyword search +############################### +sub check_srchidx { + if ((!exists $Args::args{'srchidx'}) || ($Args::args{'srchidx'} !~ /^\d+$/)) + { + Print::print_check_err( + "Invalid srchidx argument (positive numbers only)"); + } + return 0; +} + +############################### +# sort +############################### +sub check_sort { + if ((!exists $Args::args{'sort'}) || ($Args::args{'sort'} !~ /^\d+$/)) { + Print::print_check_err("Invalid sort argument (positive numbers only)"); + } + return 0; +} + +sub get_sort { + if ($Args::args{'sort'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid sort flag"); +} + +################################ +# st_mon +################################ +sub check_st_mon { + if ( (exists $Args::args{'st_mon'}) + && ($Args::args{'st_mon'} =~ /^(\d\d?)$/)) + { + if (($1 < 1) || ($1 > 12)) { + print("Invalid start month\n"); + return 1; + } + } + else { + print("Invalid start month\n"); + return 1; + } +} + +sub get_st_mon { + if ($Args::args{'st_mon'} =~ /^(\d\d?)$/) { + return $1; + } + Print::print_err("Invalid Month"); +} + +################################ +# st_year +################################ +sub check_st_year { + if ( (exists $Args::args{'st_year'}) + && ($Args::args{'st_year'} =~ /^(\d\d\d\d?)$/)) + { + if (($1 < 1970) || ($1 > 2020)) { + print("Invalid start year\n"); + return 1; + } + } + else { + print("Invalid start year\n"); + return 1; + } +} + +sub get_st_year { + if ($Args::args{'st_year'} =~ /^(\d\d\d\d)$/) { + return $1; + } + Print::print_err("Invalid Year"); +} + +################################ +# str +# search string +################################ +# This should be made more flexible +sub check_str { + if (!exists $Args::args{'str'}) { + Print::print_check_err("Missing string argument"); + } + return 0; +} + +sub get_str { + if ($Args::args{'str'} =~ /^\s*(.*)$/) { + return $1; + } + Print::print_err("Invalid String"); +} + +############################### +# submod +# Used by the tab module to identify the actual module +############################### +sub check_submod { + if ((!exists $Args::args{'submod'}) || ($Args::args{'submod'} !~ /^\d+$/)) { + Print::print_check_err( + "Invalid sub-mode argument (positive numbers only)"); + } + return 0; +} + +sub get_submod { + if ($Args::args{'submod'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid sub-mode"); +} + +################################ +# tl +############################### +sub check_tl { + if ((!exists $Args::args{'tl'}) || ($Args::args{'tl'} !~ /^$::REG_VNAME$/)) + { + Print::print_check_err( + "Invalid timeline argument (positive numbers only)"); + } + return 0; +} + +sub get_tl { + if ($Args::args{'tl'} =~ /^($::REG_VNAME)$/o) { + return $1; + } + Print::print_err("Invalid Timeline"); +} + +################################ +# ts +# time skew +################################ +sub check_ts { + if ((!exists $Args::args{'ts'}) || ($Args::args{'ts'} !~ /^$::REG_SKEW$/o)) + { + Print::print_check_err("Missing time skew argument"); + } + return 0; +} + +sub get_ts { + if ($Args::args{'ts'} =~ /^\s*($::REG_SKEW)$/o) { + return $1; + } + Print::print_err("Invalid Time Skew"); +} + +################################ +# tz +# timezone +################################ +sub check_tz { + if ( (!exists $Args::args{'tz'}) + || ($Args::args{'tz'} !~ /^$::REG_ZONE_ARGS$/o)) + { + Print::print_check_err("Missing time zone argument"); + } + return 0; +} + +sub get_tz { + if ($Args::args{'tz'} =~ /^($::REG_ZONE_ARGS)$/o) { + return $1; + } + Print::print_err("Invalid Timezone"); +} + +################################ +# unitsize +################################ +sub get_unitsize { + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $blkcat_out; + + if ($ftype eq 'blkls') { + if (exists $Caseman::mod2vol{$vol}) { + my $orig = $Caseman::mod2vol{$vol}; + my $img = $Caseman::vol2path{$orig}; + my $offset = $Caseman::vol2start{$orig}; + my $imgtype = $Caseman::vol2itype{$orig}; + + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $Caseman::vol2ftype{$orig} -s -o $offset -i $imgtype $img" + ); + $blkcat_out = <OUT>; + close(OUT); + } + + # We don't have the original image, so just set the size to 512 + else { + return 512; + } + } + elsif ($ftype eq 'swap') { + return 4096; + } + elsif ($ftype eq 'raw') { + return 512; + } + elsif ($Caseman::vol2cat{$vol} eq 'disk') { + return 512; + } + else { + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkcat' -f $ftype -s -o $offset -i $imgtype $img"); + $blkcat_out = <OUT>; + close(OUT); + } + $blkcat_out = "Error getting unit size" + if ((!defined $blkcat_out) || ($blkcat_out eq "")); + + if ($blkcat_out =~ /(\d+): Size of Addressable Unit/) { + return $1; + } + else { + Print::print_err("Error identifying block size (blkcat -s output)\n" + . "$blkcat_out\n"); + } +} + +################################ +# View - subset of module +################################ +sub check_view { + if ((!exists $Args::args{'view'}) || ($Args::args{'view'} !~ /^\d+$/)) { + Print::print_check_err("Invalid View argument (positive numbers only)"); + } + return 0; +} + +sub get_view { + if ($Args::args{'view'} =~ /^(\d+)$/) { + return $1; + } + Print::print_err("Invalid View"); +} + +############################### +# We don't allow much for the volume because this is an argument to +# the TSK programs. We keep these files only in one +# directory and for easy/simple security only allow basic names +# Symbolic links are allowed if these simple names are not desired +# +# Allowed values are A-Za-z0-9_-. +# +# The argument is the name of the image +############################### + +sub check_vol { + my $vol = shift; + if ((!exists $Args::args{$vol}) || ($Args::args{$vol} !~ /^$::REG_VNAME$/)) + { + Print::print_check_err( + "Invalid volume argument (name and number only)"); + } + return 0; +} + +sub get_vol { + my $vol = shift; + if ($Args::args{$vol} =~ /^($::REG_VNAME)$/) { + return $1; + } + Print::print_err("Invalid volume ($vol)"); +} + +1; diff --git a/lib/Caseman.pm b/lib/Caseman.pm new file mode 100644 index 0000000000000000000000000000000000000000..a34db680878b7ef6a5acec789eb50bfc4296b5ed --- /dev/null +++ b/lib/Caseman.pm @@ -0,0 +1,4161 @@ +# +# Case Management methods, including the windows and functions to read the +# config files +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Caseman; + +# If the order of these views are changed, then the order of the main +# function may have to be as well + +# Case Views +$Caseman::CASE_NEW = 1; +$Caseman::CASE_NEW_DOIT = 2; +$Caseman::CASE_OPEN = 3; +$Caseman::CASE_OPEN_LOG = 4; +$Caseman::CASE_DETAILS = 5; + +# $Caseman::CASE_DEL = 6; +my $CASE_MAX = 5; + +# Host Views +$Caseman::HOST_ADD = 7; +$Caseman::HOST_ADD_DOIT = 8; +$Caseman::HOST_OPEN = 9; +$Caseman::HOST_OPEN_LOG = 10; +$Caseman::HOST_DETAILS = 11; + +# $Caseman::HOST_DEL = 12; +my $HOST_MAX = 11; + +# Image Views +$Caseman::IMG_ADD = 13; +$Caseman::IMG_ADD_PREP = 14; +$Caseman::IMG_ADD_DOIT = 15; +$Caseman::VOL_OPEN = 16; +$Caseman::VOL_OPEN_LOG = 17; +$Caseman::VOL_DETAILS = 18; +$Caseman::IMG_DEL = 19; +$Caseman::VOL_MAKESTR = 20; +$Caseman::VOL_MAKEBLKLS = 21; +my $IMG_MAX = 21; + +# Module Variables +# %vol2par - Volume to parent volume (vol to img, str to vol) +# %vol2start - Starting sector of volume in image +# %vol2end - ending sector of volume in image +# %vol2cat - The big picture type of volume (part, disk, strings, blkls) +# %vol2ftype - The file system type (fat, dos, ntfs etc.) +# %vol2itype - The image file type (could be for a parent image) +# %vol2dtype- the disk type +# %mod2vol; # Mapping for image, given the strings or blkls +# %vol2mnt; # Mapping for mount point, given the vol name +# %vol2str; # Mapping for ASCII strings file, given the vol name +# %vol2uni; # Mapping for Unicode strings file, given the vol name +# %vol2blkls; # Mapping for blkls file, given the vol name +# %vol2path - full file path of volume +# %vol2sname - short name of volume + +sub main { + + # By default, show the case open window + $Args::args{'view'} = $Args::enc_args{'view'} = $Caseman::CASE_OPEN + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + # The only live function is for the open img + if ($::LIVE == 1) { + Args::check_inv(); + if ($view == $Caseman::VOL_OPEN) { + return vol_open(); + } + + Args::check_vol('vol'); + + # Args::check_ftype(); + # Args::check_mnt(); + + if ($view == $Caseman::VOL_OPEN_LOG) { + return vol_open_log(); + } + else { + Print::print_check_err( + "Invalid Live Analysis Case Management View"); + } + return 0; + } + + # Case functions + if ($view <= $CASE_MAX) { + if ($view == $Caseman::CASE_OPEN) { + return case_open(); + } + elsif ($view == $Caseman::CASE_NEW) { + return case_new(); + } + + Args::check_case(); + $::case_dir = "$::LOCKDIR/" . Args::get_case() . "/"; + $::case_dir =~ s/\/\//\//g; + + if ($view == $Caseman::CASE_OPEN_LOG) { + return case_open_log(); + } + elsif ($view == $Caseman::CASE_NEW_DOIT) { + return case_new_doit(); + } + elsif ($view == $Caseman::CASE_DETAILS) { + return case_details(); + } + } + + Args::check_case(); + $::case_dir = "$::LOCKDIR/" . Args::get_case() . "/"; + $::case_dir =~ s/\/\//\//g; + + # Host functions + if ($view <= $HOST_MAX) { + if ($view == $Caseman::HOST_OPEN) { + return host_open(); + } + elsif ($view == $Caseman::HOST_ADD) { + return host_add(); + } + + Args::check_host(); + $::host_dir = "$::case_dir" . Args::get_host() . "/"; + $::host_dir =~ s/\/\//\//g; + if ($view == $Caseman::HOST_ADD_DOIT) { + return host_add_doit(); + } + + Caseman::read_host_config(); + if ($view == $Caseman::HOST_OPEN_LOG) { + return host_open_log(); + } + elsif ($view == $Caseman::HOST_DETAILS) { + return host_details(); + } + } + + Args::check_host(); + $::host_dir = "$::case_dir" . Args::get_host() . "/"; + $::host_dir =~ s/\/\//\//g; + Caseman::read_host_config(); + Args::check_inv(); + + if ($view <= $IMG_MAX) { + if ($view == $Caseman::VOL_OPEN) { + return vol_open(); + } + elsif ($view == $Caseman::IMG_ADD) { + return img_add(); + } + elsif ($view == $Caseman::IMG_ADD_PREP) { + return img_add_prep(); + } + elsif ($view == $Caseman::IMG_ADD_DOIT) { + return img_add_doit(); + } + + Args::check_vol('vol'); + + if ($view == $Caseman::VOL_OPEN_LOG) { + return vol_open_log(); + } + elsif ($view == $Caseman::VOL_DETAILS) { + return vol_details(); + } + + # elsif ($view == $Caseman::IMG_DEL) { + # return img_del(); + # } + elsif ($view == $Caseman::VOL_MAKESTR) { + return vol_makestr(); + } + elsif ($view == $Caseman::VOL_MAKEBLKLS) { + return vol_makeblkls(); + } + } + + Print::print_check_err("Invalid Case Management View"); +} + +#################################################################### +# General menu Functions + +sub print_menu_tabs { + + if ($::LIVE == 1) { + print "<h2>Live Analysis Mode</h2>\n"; + } + + print "<table width=\"600\" height=\"60\" background=\"$::YEL_PIX\" " + . "border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tr>\n" + . "<td align=\"center\" width=\"200\">"; + + my $view = Args::get_view(); + + # Case Gallery Tab + if ($view == $Caseman::CASE_OPEN) { + print "<img border=0 src=\"pict/menu_t_cg_cur.jpg\" " + . "width=200 height=65 alt=\"Case Gallery (Current Mode)\">\n"; + } + elsif ($::LIVE == 1) { + print "<img border=0 src=\"pict/menu_t_cg_org.jpg\" " + . "width=200 height=65 alt=\"Case Gallery\">\n"; + } + else { + print "<a href=\"$::PROGNAME?" + . "mod=$::MOD_CASEMAN&view=$Caseman::CASE_OPEN\">" + . "<img border=0 src=\"pict/menu_t_cg_link.jpg\" " + . "width=200 height=65 alt=\"Case Gallery\"></a>\n"; + } + print "</td>\n" . "<td align=\"center\" width=\"200\">"; + + # Host Gallery Tab + # Current + if ($view == $Caseman::HOST_OPEN) { + print "<img border=0 src=\"pict/menu_t_hg_cur.jpg\" " + . "width=200 height=65 alt=\"Host Gallery (Current Mode)\">\n"; + } + + # Link + elsif (($view == $Caseman::VOL_OPEN) && ($::LIVE == 0)) { + print "<a href=\"$::PROGNAME?" + . "mod=$::MOD_CASEMAN&view=$Caseman::HOST_OPEN" + . "&case=$Args::args{'case'}\">" + . "<img border=0 src=\"pict/menu_t_hg_link.jpg\" " + . "width=200 height=65 alt=\"Host Gallery\"></a>\n"; + } + + # Non-link + else { + print "<img border=0 src=\"pict/menu_t_hg_org.jpg\" " + . "width=200 height=65 alt=\"Host Gallery (Not Available)\">\n"; + } + + print "</td>\n" . "<td align=\"center\" width=\"200\">"; + + # Host Manager Tab + # Current + if ($view == $Caseman::VOL_OPEN) { + print "<img border=0 src=\"pict/menu_t_hm_cur.jpg\" " + . "width=200 height=65 alt=\"Host Manager (Current Mode)\">\n"; + } + + # non-link + else { + print "<img border=0 src=\"pict/menu_t_hm_org.jpg\" " + . "width=200 height=65 alt=\"Host Manager (Not Available)\">\n"; + } + + print "</td>\n</tr>\n" . "</table>\n"; +} + +#################################################################### +# Case Functions + +# if no args are passed, return case config using args{'case'}, +# else use the case value passed +# +# Case config: +# In case directory with case_name.case +sub case_config_fname { + if (scalar(@_) == 1) { + my $c = shift; + return "$::LOCKDIR/" . "$c/case.aut"; + } + else { + return "$::LOCKDIR/" . "$Args::args{'case'}/case.aut"; + } +} + +# Read case config and save it to $Caseman::cvals +sub read_case_config { + return if ($::LIVE == 1); + + my $case; + + if (scalar(@_) == 1) { + $case = shift; + } + else { + $case = Args::get_case(); + } + + my $fname = case_config_fname($case); + + %Caseman::cvals = (); + + open CONFIG, "<$fname" + or die "Can't open case config file ($fname)"; + + while (<CONFIG>) { + next if ((/^\#/) || (/^\s+$/)); + s/^\s+//; + s/\s+$//; + $Caseman::cvals{$1} = Print::html_encode($2) if (/^(\S+)\s+(.*)$/); + } + close(CONFIG); + + $Caseman::cvals{'desc'} = "None Provided" + unless (exists $Caseman::cvals{'desc'}); + + $Caseman::cvals{'created'} = "unknown" + unless (exists $Caseman::cvals{'created'}); +} + +sub case_new { + Print::print_html_header("Create A New Case"); + + print <<EOF; +<br> +<br> +<center> +<img src=\"pict/menu_h_cnew.jpg\" alt=\"New Case\"> +<br><br><br> + +<table width=\"600\" background=\"$::YEL_PIX\" cellspacing=\"0\" + cellpadding=\"2\" border=0> +<form action=\"$::PROGNAME\" method=\"get\"> +<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\"> +<input type=\"hidden\" name=\"view\" value=\"$Caseman::CASE_NEW_DOIT\"> +<tr> + <td colspan=3 align=left> + 1. <b>Case Name:</b> The name of this investigation. It can contain only letters, numbers, and symbols. + </td> +</tr> +<tr> + <td> </td> + <td align=left colspan=2><input type=\"text\" name=\"case\"></td> +</tr> + +<tr><td colspan=3> </td></tr> + +<tr> + <td colspan=3 align=left> + 2. <b>Description:</b> An optional, one line description of this case. + </td> +</tr> +<tr> + <td> </td> + <td align=left colspan=2><input type=\"text\" name=\"desc\" size=32 maxlength=32></td> +</tr> + +<tr><td colspan=3> </td></tr> + +<tr> + <td colspan=3 align=left> + 3. <b>Investigator Names:</b> The optional names (with no spaces) of the investigators for this case. + </td> +</tr> +<tr> + <td> </td> + <td align=left><tt>a.</tt> <input type=\"text\" name=\"inv1\"></td> + <td align=left><tt>b.</tt> <input type=\"text\" name=\"inv2\"></td> +</tr> +<tr> + <td> </td> + <td align=left><tt>c.</tt> <input type=\"text\" name=\"inv3\"></td> + <td align=left><tt>d.</tt> <input type=\"text\" name=\"inv4\"></td> +</tr> +<tr> + <td> </td> + <td align=left><tt>e.</tt> <input type=\"text\" name=\"inv5\"></td> + <td align=left><tt>f.</tt> <input type=\"text\" name=\"inv6\"></td> +</tr> +<tr> + <td> </td> + <td align=left><tt>g.</tt> <input type=\"text\" name=\"inv7\"></td> + <td align=left><tt>h.</tt> <input type=\"text\" name=\"inv8\"></td> +</tr> +<tr> + <td> </td> + <td align=left><tt>i.</tt> <input type=\"text\" name=\"inv9\"></td> + <td align=left><tt>j.</tt> <input type=\"text\" name=\"inv10\"></td> +</tr> +</table> + +<br><br> +<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center> + <input type=\"image\" src=\"pict/menu_b_cnew.jpg\" + alt=\"Create Case\" width=\"176\" height=20 border=0> + </td> +</form> + <td align=center> + <form action=\"$::PROGNAME\" method=\"get\"> + <input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\"> + <input type=\"hidden\" name=\"view\" value=\"$Caseman::CASE_OPEN\"> + <input type=\"image\" src=\"pict/menu_b_cancel.jpg\" + alt=\"Cancel\" width=\"167\" height=20 border=0> + </form> + </td> + <td align=center><a href=\"$::HELP_URL\" + target=\"_blank\"> + <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" + width=\"167\" height=20 border=0></a> + </td> +</tr> +</table> +EOF + + Print::print_html_footer(); + return; +} + +# Create the directory and case configuration file +# Gets the input from CASE_NEW +sub case_new_doit { + Print::print_html_header("Creating Case: $Args::args{'case'}"); + my $case = $Args::args{'case'}; + + print "<h3>Creating Case: <tt>$case</tt></h3>\n"; + + # Make the directory + if (-d "$::case_dir") { + + # we can't send all of this to print_err, bc it doesn't want HTML + print "Error: $::case_dir already exists<br>" + . "Please remove the directory and its contents and try again" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::CASE_OPEN\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + Print::print_err("\n"); + } + + unless (mkdir "$::case_dir", $::MKDIR_MASK) { + Print::print_err("Error making directory $::case_dir: $!"); + } + + print "Case directory (<tt>$::case_dir</tt>) created<br>\n"; + Print::log_case_info("Case $case created"); + + my $fname = Caseman::case_config_fname(); + + open CASE_CONFIG, ">$fname" or die "Can't open case config: $fname"; + + print CASE_CONFIG "# Autopsy case config file\n" + . "# Case: $case\n\n" + . "created " + . localtime() . "\n"; + + if ((exists $Args::args{'desc'}) && ($Args::args{'desc'} ne "")) { + Print::print_err( + "Invalid Description\n" . "Use the browser's back button to fix") + if ($Args::args{'desc'} =~ /\n/); + + print CASE_CONFIG "desc $Args::args{'desc'}\n"; + } + print CASE_CONFIG "images $::IMGDIR\n"; + print CASE_CONFIG "data $::DATADIR\n"; + print CASE_CONFIG "log $::LOGDIR\n"; + print CASE_CONFIG "reports $::REPDIR\n"; + + close CASE_CONFIG; + print "Configuration file (<tt>$fname</tt>) created<br>\n"; + + my $iname = investig_fname(); + open INVES, ">>$iname" or die "Can't open investigators file: $iname"; + + my @invs; + if ( (exists $Args::args{'inv1'}) + && ($Args::args{'inv1'} ne "") + && ($Args::args{'inv1'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv2'}) + && ($Args::args{'inv2'} ne "") + && ($Args::args{'inv2'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv3'}) + && ($Args::args{'inv3'} ne "") + && ($Args::args{'inv3'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv4'}) + && ($Args::args{'inv4'} ne "") + && ($Args::args{'inv4'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv5'}) + && ($Args::args{'inv5'} ne "") + && ($Args::args{'inv5'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv6'}) + && ($Args::args{'inv6'} ne "") + && ($Args::args{'inv6'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv7'}) + && ($Args::args{'inv7'} ne "") + && ($Args::args{'inv7'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv8'}) + && ($Args::args{'inv8'} ne "") + && ($Args::args{'inv8'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv9'}) + && ($Args::args{'inv9'} ne "") + && ($Args::args{'inv9'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + if ( (exists $Args::args{'inv10'}) + && ($Args::args{'inv10'} ne "") + && ($Args::args{'inv10'} =~ /^\s*($::REG_INVESTIG)\s*$/o)) + { + print INVES "$1\n"; + push @invs, $1; + } + + close(INVES); + + Print::log_session_info("Case $case created"); + + print "<br><br>We must now create a host for this case.\n"; + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_ADD\">\n" + . "<input type=\"hidden\" name=\"case\" value=\"$case\">\n"; + + if (scalar @invs == 0) { + print "<option hiddden name=\"inv\" value=\"unknown\">\n"; + } + else { + print "<br><br>Please select your name from the list: " + . "<select name=\"inv\" size=\"1\">\n"; + + foreach $i (@invs) { + print "<option value=\"$i\">$i</option>\n"; + } + print "</select>\n"; + } + + print "<br><br>" + . "<input type=\"image\" src=\"pict/menu_b_hnew.jpg\" alt=\"Add New Host\" " + . "height=20 border=\"0\"></form>\n"; + + Print::print_html_footer(); + return; +} + +# Open a Case +# This provides a form with a list of options +sub case_open { + Print::print_html_header("Open A Case"); + + # Read the directories of the Evidence Locker into an array + # Verify that there is a config file in the directory + my @cases; + opendir CASES, $::LOCKDIR or die "Can't open $::LOCKDIR directory: $!"; + foreach my $c (readdir CASES) { + next if (($c eq '.') || ($c eq '..')); + my $cfile = Caseman::case_config_fname($c); + + push @cases, $c + if ((-d "$::LOCKDIR/$c") && (-e "$cfile")); + } + closedir CASES; + + print "<br><br><center>"; + + # Were there any cases? + if (scalar @cases == 0) { + print "No cases exist in <tt>$::LOCKDIR</tt><br><br>\n" + . "Select the New Case button below to make one<br>\n"; + } + else { + + print "Select the case to open or create a new one<br>\n"; + + print_menu_tabs(); + + print "<table width=\"600\" background=\"$::YEL_PIX\" " + . " cellspacing=\"0\" cellpadding=\"2\" border=0>\n"; + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::CASE_OPEN_LOG\">\n" + . Args::make_hidden() + . "<tr><th>Name</th>" + . "<th>Description</th>" + . "<td> </td></tr>\n"; + + my $first = 0; + foreach my $c (@cases) { + + print "<tr><td align=\"left\">" + . "<input type=\"radio\" name=\"case\" value=$c"; + if ($first == 0) { + print " CHECKED"; + $first = 1; + } + print ">" . Print::html_encode($c) . "</td>"; + + Caseman::read_case_config($c); + + print "<td>$Caseman::cvals{'desc'}</td>" + . "<td align=center>" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::CASE_DETAILS&case=$c\">" + . "details</a></td>" + . "</tr>\n"; + } + print "</table>\n"; + } + + print "<br><br>" + . "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr>\n"; + + # Print the OK button if there were cases + if (scalar @cases != 0) { + print "<td align=center>" + . "<input type=\"image\" src=\"pict/menu_b_ok.jpg\" " + . "width=167 height=20 alt=\"Ok\" border=0>" + . "</form></td>\n\n"; + } + + # Print a 'New Case' Button + print "<td align=center>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::CASE_NEW\">\n" + . "<input type=\"image\" src=\"pict/menu_b_cnew.jpg\" " + . "width=167 height=20 alt=\"New Case\" border=0>\n" + . "</form></td>" + . + + # Print a Menu Button + "<td align=center>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"image\" src=\"pict/menu_b_menu.jpg\" " + . "width=167 height=20 alt=\"Main Menu\" border=0>\n" + . "</form></td></tr></table>\n"; + + print "<table width=600 cellspacing=\"0\" cellpadding=\"2\">\n<tr>" + . "<td> </td>\n" + . "<td align=center width=200><a href=\"$::HELP_URL\" " + . " target=\"_blank\">" + . "<img src=\"pict/menu_b_help.jpg\" alt=\"Help\" " + . "width=\"167\" height=20 border=0>" + . "</a></td>" + . "<td> </td>\n" + . "</tr>\n" + . "</table>"; + + Print::print_html_footer(); + return; +} + +# Log that a given case was opened and then proceed to open a host +sub case_open_log { + Print::log_session_info("Case $Args::args{'case'} opened"); + Print::log_case_info("Case $Args::args{'case'} opened"); + $Args::args{'view'} = $Args::enc_args{'view'} = $Caseman::HOST_OPEN; + host_open(); +} + +# Display Case Details +sub case_details { + + Print::print_html_header("Details of $Args::args{'case'}"); + + read_case_config(); + + print "<br><br>" + . "<center>" + . "<img src=\"pict/menu_h_cdet.jpg\" alt=\"Case Details\">" + . "<br><br><br>\n" + . "<table width=\"600\" cellspacing=\"0\" background=\"$::YEL_PIX\" " + . "cellpadding=\"2\" border=0>\n" + . " <tr><td align=\"right\" width=300><b>Name:</b></td>" + . "<td align=\"left\" width=300><tt>$Args::args{'case'}</tt></td></tr>\n" + . + + # Description + " <tr><td align=\"right\"><b>Description:</b></td>" + . "<td align=\"left\"><tt>$Caseman::cvals{'desc'}</tt></td></tr>\n" + . " <tr><td align=\"right\"><b>Created:</b></td>" + . "<td align=\"left\"><tt>$Caseman::cvals{'created'}</tt></td></tr>\n"; + + # Display the valid investigators + my @invs = read_invest(); + my $cnt = 0; + print " <tr><td colspan=\"2\"> </td></tr>\n" + if (scalar @invs > 0); + + foreach my $i (@invs) { + if ($cnt == 0) { + print " <tr><td align=\"right\"><b>Investigators:</b></td>"; + $cnt++; + } + else { + print " <tr><td> </td>"; + } + print "<td align=\"left\"><tt>" + . Print::html_encode($i) + . "</tt></td></tr>\n"; + } + + print "</table>\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::CASE_OPEN\">" + . "<img src=\"pict/menu_b_ok.jpg\" alt=\"Ok\" " + . "width=\"167\" height=20 border=\"0\"></a>"; + + Print::print_html_footer(); + return 0; +} + +#################################################################### +# Host Functions + +# if no args are passed, return host config using args{'host'}, +# else use the host value passed +sub host_config_fname { + if (scalar(@_) == 1) { + my $h = shift; + return "$::case_dir" . "$h/host.aut"; + } + else { + return "$::host_dir" . "host.aut"; + } +} + +# Converts the original single image host config to the volume-based config +sub convert_host_config { + + return if ($::LIVE == 1); + + my $host = Args::get_host(); + + Print::log_host_info("Converting host config files"); + print STDERR "Converting host config file to new format\n"; + + # The file to convert + my $cfile = host_config_fname(); + unless (open(FILE, $cfile)) { + Print::print_check_err("Error opening $cfile"); + } + + my $tmpcfile = "$::host_dir" . "host-convert.aut"; + unless (open(FILE_TMP, ">>$tmpcfile")) { + Print::print_check_err("Error opening $tmpcfile"); + } + + my $img_cnt = 0; + my $vol_cnt = 0; + $img2vol{'qazwsxedc'} = ""; # stores the image path to partition / file name + $img2vol2{'qazwsxedc'} = + ""; # stores the image path to file name (no partitions) + + while (<FILE>) { + if ((/^\#/) || (/^\s+$/)) { + print FILE_TMP $_; + next; + } + + # remove whitespace + s/^\s+//; + s/\s+$//; + + # normal file system image entry + # + # 'image images/hda1.dd openbsd /usr + if (/^image\s+($::REG_IMG)\s+([\w\-]+)\s+([\w\-\_\.\/:\\]+)$/o) { + + my $i = $1; + my $t = $2; + my $r = $3; + + # Add trailing / to original mount point if needed + if (($r !~ /.*?\/$/) && ($r !~ /.*?\\$/)) { + $r .= '/'; + } + my $vnum = "vol" . $vol_cnt; + my $inum = "img" . $img_cnt; + $img2vol{$i} = $vnum; + $img2vol2{$i} = $inum; + + print FILE_TMP "image $inum raw $i\n"; + print FILE_TMP "part $vnum $inum 0 0 $t $r\n"; + + $img_cnt++; + $vol_cnt++; + } + + # swap + # swap images/hda3.dd + elsif (/^swap\s+($::REG_IMG)\s*$/o) { + my $i = $1; + + my $vnum = "vol" . $vol_cnt; + my $inum = "img" . $img_cnt; + $img2vol{$i} = $vnum; + $img2vol2{$i} = $inum; + + print FILE_TMP "image $inum raw $i\n"; + print FILE_TMP "part $vnum $inum 0 0 swap\n"; + $img_cnt++; + $vol_cnt++; + + } + + # raw + # raw images/hda3.dd + elsif (/^raw\s+($::REG_IMG)\s*$/o) { + my $i = $1; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "img" . $img_cnt; + + print FILE_TMP "image img" . $img_cnt . " raw $i\n"; + print FILE_TMP "part vol" . $vol_cnt . " img" . $img_cnt + . " 0 0 raw\n"; + $img_cnt++; + $vol_cnt++; + } + + # entry for a strings or blkls file + # + # strings data/hda1.str images/hda1.dd + elsif (/^strings\s+($::REG_IMG)\s+($::REG_IMG)$/o) { + my $i = $1; + my $o = $2; + + if (exists $img2vol{$o}) { + my $vname = $img2vol{$o}; + print FILE_TMP "strings vol" . $vol_cnt . " $vname $i\n"; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "vol" . $vol_cnt; + } + else { + print STDERR "Error: Volume for strings $o not found<br>\n"; + } + $vol_cnt++; + } + + # entry for a strings or blkls file + # + # unistrings data/hda1.str images/hda1.dd + elsif (/^unistrings\s+($::REG_IMG)\s+($::REG_IMG)$/o) { + my $i = $1; + my $o = $2; + + if (exists $img2vol{$o}) { + my $vname = $img2vol{$o}; + print FILE_TMP "unistrings vol" . $vol_cnt + . " $vname $i\n"; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "vol" . $vol_cnt; + } + else { + print STDERR + "Error: Volume for unicode strings $o not found<br>\n"; + } + $vol_cnt++; + } + + # blkls entry + # blkls data/image.blkls [images/image.dd] + elsif (/^blkls\s+($::REG_IMG)\s*($::REG_IMG)$/o) { + my $i = $1; + my $o = $2; + + $img2vol{$i} = "vol" . $vol_cnt; + + if (exists $img2vol{$o}) { + my $vname = $img2vol{$o}; + print FILE_TMP "blkls vol" . $vol_cnt . " $vname $i\n"; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "vol" . $vol_cnt; + } + else { + print STDERR "Error: Volume for blkls $o not found<br>\n"; + } + $vol_cnt++; + } + + # body data/body.txt + elsif (/^body\s+($::REG_IMG)$/o) { + my $i = $1; + print FILE_TMP "body vol" . $vol_cnt . " $i\n"; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "vol" . $vol_cnt; + + $vol_cnt++; + } + + # timeline data/timeline.txt + elsif (/^timeline\s+($::REG_IMG)$/o) { + my $i = $1; + print FILE_TMP "timeline vol" . $vol_cnt . " $i\n"; + $img2vol{$i} = "vol" . $vol_cnt; + $img2vol2{$i} = "vol" . $vol_cnt; + $vol_cnt++; + } + + # timezone XYZ + elsif (/^timezone\s+($::REG_ZONE_ARGS)$/o) { + print FILE_TMP "$_\n"; + } + + # timeskew XYZ + elsif (/^timeskew\s+($::REG_SKEW)$/o) { + print FILE_TMP "$_\n"; + } + + # desc XYZ + elsif (/^desc\s+(.*)$/) { + print FILE_TMP "$_\n"; + } + + # hash databases + elsif (/^alert_db\s+'(.*)'$/) { + print FILE_TMP "$_\n"; + } + elsif (/^exclude_db\s+'(.*)'$/) { + print FILE_TMP "$_\n"; + } + else { + my $msg = + "Error: invalid entry in $cfile:$." . "\n" + . "image path fs_type mnt_point\n" + . "strings path orig_img\n" + . "blkls path [orig_img]\n" + . "body path\n" + . "timeline path\n" + . "timezone TZ\n" + . "desc DESCRIPTION\n"; + Print::print_check_err($msg); + } + } + + close(FILE); + close(FILE_TMP); + unless (rename $cfile, $cfile . ".bak") { + print STDERR "Error backing up original host config file\n"; + } + unless (rename $tmpcfile, $cfile) { + print STDERR "Error renaming new host config file\n"; + } + + Notes::convert(\%img2vol); + Hash::convert(\%img2vol2); +} + +# reads host config file and sets global hash values for images and other +sub read_host_config { + + if ($::LIVE == 1) { + %Caseman::mod2vol = (); + %Caseman::vol2str = (); + %Caseman::vol2uni = (); + %Caseman::vol2blkls = (); + $Caseman::tz = ""; + $Caseman::ts = 0; + $Caseman::host_desc = ""; + $Caseman::exclude_db = ""; + $Caseman::alert_db = ""; + return; + } + + my $host = Args::get_host(); + + my $cfile = host_config_fname(); + restart: + unless (open(FILE, $cfile)) { + Print::print_check_err("Error opening $cfile"); + } + + %Caseman::vol2mnt = (); + %Caseman::vol2ftype = (); + %Caseman::vol2dtype = (); + %Caseman::vol2cat = (); + %Caseman::mod2vol = (); + %Caseman::vol2str = (); + %Caseman::vol2uni = (); + %Caseman::vol2blkls = (); + %Caseman::vol2par = (); + %Caseman::vol2start = (); + %Caseman::vol2end = (); + $Caseman::vol2path = (); + $Caseman::vol2sname = (); + + $Caseman::tz = ""; + $Caseman::ts = 0; + $Caseman::host_desc = ""; + $Caseman::exclude_db = ""; + $Caseman::alert_db = ""; + + while (<FILE>) { + next if ((/^\#/) || (/^\s+$/)); + + # remove whitespace + s/^\s+//; + s/\s+$//; + + # old file system image entry + # + # 'image images/hda1.dd openbsd /usr + if (/^image\s+($::REG_IMG)\s+([\w\-]+)\s+([\w\-\_\.\/:\\]+)$/o) { + + close(FILE); + convert_host_config(); + goto restart; + } + elsif ( + /^image\s+($::REG_INAME)\s+($::REG_IMGTYPE)\s+($::REG_IMG_CONFIG)$/o + ) + { + my $me = $1; + my $t = $2; + my $i = $3; + + unless ((-e "$::host_dir$i") + || ((-l "$::host_dir$i") && (-e readlink "$::host_dir$i"))) + { + Print::print_check_err( + "Error: image $i in ${host}.host:$. not found: " + . "$::host_dir" + . "$i \nEdit the config file and refresh your browser\n" + . "(Or your version of Perl does not support large files)" + ); + } + + if (exists $Caseman::vol2path{$me}) { + $Caseman::vol2path{$me} .= " \'$::host_dir" . "$i\'"; + } + else { + $Caseman::vol2path{$me} = "\'$::host_dir" . "$i\'"; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + + $Caseman::vol2par{$me} = ""; + $Caseman::vol2cat{$me} = "image"; + $Caseman::vol2itype{$me} = $t; + $Caseman::vol2ftype{$me} = ""; + + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + } + } + elsif ( +/^part\s+($::REG_VNAME)\s+($::REG_INAME)\s+(\d+)\s+(\d+)\s+($::REG_FTYPE)\s*([\w\-\_\.\/:\\]+)?$/o + ) + { + my $par = $2; + my $me = $1; + my $s = $3; + my $e = $4; + my $t = $5; + my $r = $6; # Not defined for swap and raw + + unless (exists $Fs::addr_unit{$t}) { + Print::print_check_err( + "Error: unknown type: $t in host config: $." + . "\nEdit the file and refresh your browser"); + } + + if (exists $Caseman::vol2itype{$par}) { + $Caseman::vol2itype{$me} = $Caseman::vol2itype{$par}; + } + else { + Print::print_check_err( +"Error: Image $par for partition $me was not found in config: $." + . "\nEdit the file and refresh your browser"); + } + + $Caseman::vol2ftype{$me} = $t; + $Caseman::vol2cat{$me} = "part"; + $Caseman::vol2start{$me} = $s; + $Caseman::vol2end{$me} = $e; + + # Add trailing / to original mount point if needed + if ((defined $r) && ($r !~ /.*?\/$/) && ($r !~ /.*?\\$/)) { + $r .= '/'; + } + $Caseman::vol2mnt{$me} = $r; + $Caseman::vol2par{$me} = $par; + $Caseman::vol2path{$me} = $Caseman::vol2path{$par}; + $Caseman::vol2sname{$me} = + $Caseman::vol2sname{$par} . "-" . $s . "-" . $e; + } + elsif (/^disk\s+($::REG_VNAME)\s+($::REG_INAME)\s+($::REG_FTYPE)?$/o) { + my $par = $2; + my $me = $1; + my $t = $3; + + unless (exists $Vs::type{$t}) { + Print::print_check_err( + "Error: unknown volume system type: $t in host config: $." + . "\nEdit the file and refresh your browser"); + } + + if (exists $Caseman::vol2itype{$par}) { + $Caseman::vol2itype{$me} = $Caseman::vol2itype{$par}; + } + else { + Print::print_check_err( +"Error: Image $par for disk $me was not found in config: $.\n" + . "Edit the file and refresh your browser"); + } + + $Caseman::vol2ftype{$me} = "raw"; + $Caseman::vol2dtype{$me} = $t; + $Caseman::vol2cat{$me} = "disk"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2mnt{$me} = ""; + $Caseman::vol2par{$me} = $par; + $Caseman::vol2path{$me} = $Caseman::vol2path{$par}; + $Caseman::vol2sname{$me} = $Caseman::vol2sname{$par} . "-disk"; + } + + # entry for a strings or blkls file + # + # strings data/hda1.str volX + elsif (/^strings\s+($::REG_VNAME)\s+($::REG_VNAME)\s+($::REG_IMG)$/o) { + my $i = $3; + my $par = $2; + my $me = $1; + + unless ((-e "$::host_dir$i") + || ((-l "$::host_dir$i") && (-e readlink "$::host_dir$i"))) + { + Print::print_check_err("Error: strings file not found: " + . "$::host_dir$i\nEdit host config in $::host_dir and refresh your browser" + ); + } + + unless (exists $Caseman::vol2cat{$par}) { + Print::print_check_err( +"Error: Volume $par for strings $me was not found in config: $." + . "\nEdit the file and refresh your browser"); + } + + $Caseman::vol2ftype{$me} = "strings"; + $Caseman::vol2cat{$me} = "mod"; + $Caseman::vol2itype{$me} = "raw"; + $Caseman::mod2vol{$me} = $par; + $Caseman::vol2str{$par} = $me; + $Caseman::vol2par{$me} = $par; + $Caseman::vol2path{$me} = "\'$::host_dir" . "$i\'"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + + } + + # entry for a strings or blkls file + # + # unistrings data/hda1.str volX + elsif (/^unistrings\s+($::REG_VNAME)\s+($::REG_VNAME)\s+($::REG_IMG)$/o) + { + my $i = $3; + my $par = $2; + my $me = $1; + + unless ((-e "$::host_dir$i") + || ((-l "$::host_dir$i") && (-e readlink "$::host_dir$i"))) + { + Print::print_check_err("Error: Unicode strings file not found: " + . "$::host_dir$i\nEdit host config in $::host_dir and refresh your browser" + ); + } + + unless (exists $Caseman::vol2cat{$par}) { + Print::print_check_err( +"Error: Volume $par for unistrings $me was not found in config: $." + . "\nEdit the file and refresh your browser"); + } + + $Caseman::vol2ftype{$me} = "strings"; + $Caseman::vol2cat{$me} = "mod"; + $Caseman::vol2itype{$me} = "raw"; + $Caseman::mod2vol{$me} = $par; + $Caseman::vol2uni{$par} = $me; + $Caseman::vol2par{$me} = $par; + $Caseman::vol2path{$me} = "\'$::host_dir" . "$i\'"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + } + + # blkls entry + # blkls themname myname + elsif ((/^blkls\s+($::REG_VNAME)\s+($::REG_VNAME)\s+($::REG_IMG)$/o) || + (/^dls\s+($::REG_VNAME)\s+($::REG_VNAME)\s+($::REG_IMG)$/o)) { + my $i = $3; + my $par = $2; + my $me = $1; + + unless ( + (-e "$::host_dir$i") + || ( (-l "$::host_dir$i") + && (-e readlink "$::host_dir$i")) + ) + { + Print::print_check_err("Error: blkls file not found: " + . "$::host_dir$i \nEdit host config in $::host_dir and refresh your browser" + ); + } + + unless (exists $Caseman::vol2cat{$par}) { + Print::print_check_err( +"Error: Volume $par for blkls $me was not found in config: $." + . "\nEdit the file and refresh your browser"); + } + + $Caseman::vol2ftype{$me} = "blkls"; + $Caseman::vol2cat{$me} = "mod"; + $Caseman::vol2itype{$me} = "raw"; + $Caseman::vol2mnt{$me} = ""; + $Caseman::vol2par{$me} = $par; + $Caseman::mod2vol{$me} = $par; + $Caseman::vol2blkls{$par} = $me; + $Caseman::vol2path{$me} = "\'$::host_dir" . "$i\'"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + + } + + # body data/body.txt + elsif (/^body\s+($::REG_VNAME)\s+($::REG_IMG)$/o) { + my $me = $1; + my $i = $2; + + unless ((-e "$::host_dir$i") + || ((-l "$::host_dir$i") && (-e readlink "$::host_dir$i"))) + { + Print::print_check_err("Error: body file not found: " + . "$::host_dir$i <br>Edit host config in $::host_dir and refresh your browser" + ); + } + + $Caseman::vol2cat{$me} = "timeline"; + $Caseman::vol2ftype{$me} = "body"; + $Caseman::vol2itype{$me} = "raw"; + $Caseman::vol2path{$me} = "\'$::host_dir" . "$i\'"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + } + + # timeline data/timeline.txt + elsif (/^timeline\s+($::REG_VNAME)\s+($::REG_IMG)$/o) { + my $me = $1; + my $i = $2; + + unless ((-e "$::host_dir$i") + || ((-l "$::host_dir$i") && (-e readlink "$::host_dir$i"))) + { + Print::print_check_err("Error: timeline file not found: " + . "$::host_dir$i \nEdit host config in $::host_dir and refresh your browser" + ); + } + + $Caseman::vol2cat{$me} = "timeline"; + $Caseman::vol2ftype{$me} = "timeline"; + $Caseman::vol2itype{$me} = "raw"; + +# We do not add the quotes to the path for timeline because it is opened only by the Perl code and it doesn't like the quotes + $Caseman::vol2path{$me} = "$::host_dir" . "$i"; + $Caseman::vol2start{$me} = 0; + $Caseman::vol2end{$me} = 0; + $Caseman::vol2sname{$me} = $i; + $Caseman::vol2sname{$me} = $1 if ($i =~ /\/($::REG_FILE)$/); + + } + + # timezone XYZ + elsif (/^timezone\s+($::REG_ZONE_ARGS)$/o) { + $Caseman::tz = "\'$1\'"; + } + + # timeskew XYZ + elsif (/^timeskew\s+($::REG_SKEW)$/o) { + $Caseman::ts = "\'$1\'"; + } + + # desc XYZ + elsif (/^desc\s+(.*)$/) { + $Caseman::host_desc = Print::html_encode($1); + } + + # hash databases + elsif (/^alert_db\s+'($::REG_HASHDB)'$/) { + $Caseman::alert_db = "$1"; + } + elsif (/^exclude_db\s+'($::REG_HASHDB)'$/) { + $Caseman::exclude_db = "$1"; + } + else { + my $msg = "Error: invalid entry in $cfile:$." . "\n" . "$_\n"; + Print::print_check_err($msg); + } + } + close(FILE); +} + +# Add a new image entry to the host config and return its id +sub add_img_host_config { + my $type = shift; + my $other = shift; + my $id = shift; + + my $read = host_config_fname(); + my $write = $read . "-" . rand(); + + unless (open(READ, "<$read")) { + Print::print_check_err("Error opening $read"); + } + + unless (open(WRITE, ">$write")) { + Print::print_check_err("Error opening $write"); + } + + my $maxcnt = 0; + + while (<READ>) { + s/^\s+//; + s/\s+$//; + if (/^\w+\s+img(\d+)\s+.*$/) { + $maxcnt = $1 if ($1 > $maxcnt); + } + print WRITE "$_\n"; + } + $maxcnt++; + if ($id eq "") { + $id = "img" . $maxcnt; + } + print WRITE "$type $id $other\n"; + + Print::log_host_info("Image added: $type $id $other"); + + close(READ); + close(WRITE); + unless (rename $write, $read) { + print STDERR "Error renaming temp host config file\n"; + } + + return $id; +} + +# Add a new volume entry to the host config and return its id +sub add_vol_host_config { + my $type = shift; + my $other = shift; + + my $read = host_config_fname(); + my $write = $read . "-" . rand(); + + unless (open(READ, "<$read")) { + Print::print_check_err("Error opening $read"); + } + + unless (open(WRITE, ">$write")) { + Print::print_check_err("Error opening $write"); + } + + # We want to find the max count ... not just the unused + # ones because we could end up reusing one and that could + # mess up the notes + my $maxcnt = 0; + + while (<READ>) { + s/^\s+//; + s/\s+$//; + if (/^\w+\s+vol(\d+)\s+.*$/) { + $maxcnt = $1 if ($1 > $maxcnt); + } + print WRITE "$_\n"; + } + $maxcnt++; + print WRITE "$type vol" . $maxcnt . " $other\n"; + Print::log_host_info("Volume added: $type vol" . $maxcnt . " $other"); + + close(READ); + close(WRITE); + unless (rename $write, $read) { + print STDERR "Error renaming temp host config file\n"; + } + + return "vol" . $maxcnt; +} + +# Delete anentry from the host config +# DOES NOT WORK RIGHT NOW +sub del_host_config_BLAH { + return; + my $type = shift; + my $other = shift; + + my $read = host_config_fname(); + my $write = $read . "-" . rand(); + + unless (open(READ, "<$read")) { + Print::print_check_err("Error opening $read"); + } + + unless (open(WRITE, ">$write")) { + Print::print_check_err("Error opening $write"); + } + + while (<READ>) { + s/^\s+//; + s/\s+$//; + print WRITE "$_\n" + unless ((/^(\w+)\s+($::REG_IMG)/o) + && ($2 eq $img) + && ($1 ne 'desc') + && ($1 ne 'timezone')); + } + + if ($type ne "") { + if (defined $ref) { + print WRITE "$type $img $ref\n"; + } + else { + print WRITE "$type $img\n"; + } + } + + close(READ); + close(WRITE); + unless (rename $write, $read) { + print STDERR "Error renaming temp host config file\n"; + } + + return; +} + +# Given the image and md5, it is saved to the MD5.txt file and any other +# references to the image are removed +# +# if $md5 is "", then nothing is written +sub update_md5 { + my $vol = shift; + my $md5 = shift; + $md5 =~ tr/[a-f]/[A-F]/; + + my $read = "$::host_dir/md5.txt"; + my $write = $read . "-" . rand(); + + unless (open(WRITE, ">$write")) { + Print::print_check_err("Error opening $write"); + } + + if (-e "$read") { + unless (open(READ, "<$read")) { + Print::print_check_err("Error opening $read"); + } + + while (<READ>) { + s/^\s+//; + s/\s+$//; + print WRITE "$_\n" + unless ((/^$::REG_MD5\s+($::REG_VNAME)/o) && ($1 eq $vol)); + } + close(READ); + } + + print WRITE "$md5 $vol\n" if ($md5 ne ""); + + close(WRITE); + + unless (rename $write, $read) { + print STDERR "Error renaming temp MD5 hash file\n"; + } + return; +} + +sub host_add { + Print::print_html_header("Add A New Host To $Args::args{'case'}"); + + print "<b>Case: </b> $Args::args{'case'}<br><br>\n"; + + print "<center>" + . "<img src=\"pict/menu_h_hnew.jpg\" alt=\"Add Host\">" + . "<br><br><br>\n"; + + print "<table width=\"600\" cellspacing=\"0\" background=\"$::YEL_PIX\" " + . "cellpadding=\"2\" border=0>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_ADD_DOIT\">\n" + . Args::make_hidden() + . "<tr><td colspan=\"2\"> </td></tr>" + . + + # Host name +"<tr><td align=\"left\" colspan=2>1. <b>Host Name:</b> The name of the computer being investigated. It can contain only letters, numbers, and symbols.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\"><input type=\"text\" name=\"host\" size=32 maxlength=32 value=\"host1\"></td></tr>\n" + . + + # Description +"<tr><td align=\"left\" colspan=2>2. <b>Description:</b> An optional one-line description or note about this computer.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\">" + . "<input type=\"text\" name=\"desc\" size=32 maxlength=32></td></tr>\n" + . + + # Empty line + "<tr><td colspan=\"2\"> </td></tr>" . + + # Timezone +"<tr><td align=\"left\" colspan=2>3. <b>Time zone:</b> An optional timezone value (i.e. EST5EDT). If not given, it defaults to the local setting. A list of time zones can be found in the help files.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\">" + . "<input type=\"text\" name=\"tz\" size=16 maxlength=64></td></tr>\n" + . + + # Timeskew +"<tr><td align=\"left\" colspan=2>4. <b>Timeskew Adjustment:</b> An optional value to describe how many seconds this computer's clock was out of sync. For example, if the computer was 10 seconds fast, then enter -10 to compensate.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\">" + . "<input type=\"text\" name=\"ts\" size=8 maxlength=16 value=\"0\">" + . "</td></tr>\n" + . + + # Spacer + "<tr><td colspan=\"2\"> </td></tr>" . + + # Alert +"<tr><td align=\"left\" colspan=2>5. <b>Path of Alert Hash Database:</b> An optional hash database of known bad files.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\">" + . "<input type=\"text\" name=\"alert_db\" size=32 maxlength=512>" + . "</td></tr>\n" + . + + # Ignore +"<tr><td align=\"left\" colspan=2>6. <b>Path of Ignore Hash Database:</b> An optional hash database of known good files.</td></tr>" + . "<tr><td align=\"left\"> </td>" + . "<td align=\"left\">" + . "<input type=\"text\" name=\"exclude_db\" size=32 maxlength=512>" + . "</td></tr>\n" + . + + # Spacer + "<tr><td colspan=\"2\"> </td></tr>" . "</table>\n"; + + if (exists $Args::args{'inv'}) { + print + "<input type=\"hidden\" name=\"inv\" value=\"$Args::args{'inv'}\">\n"; + } + + # Ok Button + print "<br><br><table width=\"600\" cellspacing=\"8\" cellpadding=\"2\">\n" + . "<tr><td align=center>" + . "<input type=\"image\" src=\"pict/menu_b_hnew.jpg\" " + . "width=167 height=20 alt=\"Add Host\" border=0>\n" + . "</form></td>\n" + . + + # Cancel Button + "<td align=center>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_OPEN\">\n" + . "<input type=\"hidden\" name=\"case\" value=\"$Args::args{'case'}\">\n" + . "<input type=\"image\" src=\"pict/menu_b_cancel.jpg\" " + . "alt=\"Cancel\" width=\"167\" height=20 border=0>\n" + . "</form></td>\n" + . + + # Help Button + "<td align=center><a href=\"$::HELP_URL\" " + . "target=\"_blank\">" + . "<img src=\"pict/menu_b_help.jpg\" alt=\"Help\" " + . "width=\"167\" height=20 border=0></a>" + . "</td></tr>\n" + . "</table>"; + + Print::print_html_footer(); + + return 0; +} + +# Make the directories and config files for a host +sub host_add_doit { + Args::check_tz() + if ((exists $Args::args{'tz'}) && ($Args::args{'tz'} ne "")); + Args::check_ts(); + Print::print_html_header( + "Adding Host $Args::args{'host'} to $Args::args{'case'}"); + + print "<h3>Adding host: <tt>$Args::args{'host'}</tt> to " + . "case <tt>$Args::args{'case'}</tt></h3>\n"; + + # Do some sanity checks before we start making the directories and such + if ( (exists $Args::args{'alert_db'}) + && ($Args::args{'alert_db'} ne "")) + { + + unless ($Args::args{'alert_db'} =~ /^$::REG_HASHDB$/o) { + print "Invalid Alert Database path\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_ADD&case=$Args::args{'case'}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + return 1; + } + + unless (-e "$Args::args{'alert_db'}") { + print "Alert Database Not Found: $Args::args{'alert_db'}" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_ADD&case=$Args::args{'case'}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + return 1; + } + } + + if ( (exists $Args::args{'exclude_db'}) + && ($Args::args{'exclude_db'} ne "")) + { + unless ($Args::args{'exclude_db'} =~ /^$::REG_HASHDB$/o) { + print "Invalid Exclude Database path\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_ADD&case=$Args::args{'case'}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + return 1; + } + + unless (-e "$Args::args{'exclude_db'}") { + print "Exclude Database Not Found: $Args::args{'exclude_db'}" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_ADD&case=$Args::args{'case'}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + return 1; + } + } + + # Make the directory + if (-d "$::host_dir") { + + print "Error: $::host_dir already exists<br>" + . "Please remove the directory and its contents and try again" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_OPEN&case=$Args::enc_args{'case'}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + Print::print_err("\n"); + } + unless (mkdir "$::host_dir", $::MKDIR_MASK) { + Print::print_err("Error making directory $::host_dir: $!"); + } + + print "Host Directory (<tt>$::host_dir</tt>) created<p>\n"; + Print::log_case_info("Host $Args::args{'host'} added to case"); + + # Images directory + unless (mkdir "$::host_dir" . "$::IMGDIR", $::MKDIR_MASK) { + rmdir "$::host_dir"; + Print::print_err("Error making $::host_dir" . "$::IMGDIR"); + } + + # Output Directory + unless (mkdir "$::host_dir" . "$::DATADIR", $::MKDIR_MASK) { + rmdir "$::host_dir" . "$::IMGDIR"; + rmdir "$::host_dir"; + Print::print_err("Error making $::host_dir" . "$::DATADIR"); + } + + # Log Directory + unless (mkdir "$::host_dir" . "$::LOGDIR", $::MKDIR_MASK) { + rmdir "$::host_dir" . "$::DATADIR"; + rmdir "$::host_dir" . "$::IMGDIR"; + rmdir "$::host_dir"; + Print::print_err("Error making $::host_dir" . "$::LOGDIR"); + } + + # Reports directory + unless (mkdir "$::host_dir" . "$::REPDIR", $::MKDIR_MASK) { + rmdir "$::host_dir" . "$::LOGDIR"; + rmdir "$::host_dir" . "$::DATADIR"; + rmdir "$::host_dir" . "$::IMGDIR"; + rmdir "$::host_dir"; + Print::print_err("Error making $::host_dir" . "$::REPDIR"); + } + + # Make a directory for mounting the image in loopback + unless (mkdir "$::host_dir" . "mnt", $::MKDIR_MASK) { + rmdir "$::host_dir" . "$::REPDIR"; + rmdir "$::host_dir" . "$::LOGDIR"; + rmdir "$::host_dir" . "$::DATADIR"; + rmdir "$::host_dir" . "$::IMGDIR"; + rmdir "$::host_dir"; + Print::print_err("Error making $::host_dir" . "mnt"); + } + + Print::log_host_info( + "Host $Args::args{'host'} added to case $Args::args{'case'}"); + + # Create config file + my $fname = Caseman::host_config_fname(); + open HOST_CONFIG, ">$fname" or die "Can't open host config: $fname"; + + print HOST_CONFIG "# Autopsy host config file\n" + . "# Case: $Args::args{'case'} Host: $Args::args{'host'}\n" + . "# Created: " + . localtime() . "\n\n"; + + if ((exists $Args::args{'desc'}) && ($Args::args{'desc'} ne "")) { + Print::print_err( + "Invalid Description\n" . "Use the browser's back button to fix") + if ($Args::args{'desc'} =~ /\n/); + + print HOST_CONFIG "desc $Args::args{'desc'}\n"; + } + + print HOST_CONFIG "timezone " . Args::get_tz() . "\n" + if ((exists $Args::args{'tz'}) && ($Args::args{'tz'} ne "")); + print HOST_CONFIG "timeskew " . Args::get_ts() . "\n"; + + if ( (exists $Args::args{'alert_db'}) + && ($Args::args{'alert_db'} ne "")) + { + + # Index it if it is not + unless (-e "$Args::args{'alert_db'}-md5.idx") { + print +"Alert Database has not been indexed - it will be as an md5sum file<br>\n"; + + print "<hr>\n"; + Hash::index_md5sum($Args::args{'alert_db'}); + print "<hr>\n"; + } + + # only print it if it was successful + print HOST_CONFIG "alert_db \'$Args::args{'alert_db'}\'\n" + if (-e "$Args::args{'alert_db'}-md5.idx"); + } + + if ( (exists $Args::args{'exclude_db'}) + && ($Args::args{'exclude_db'} ne "")) + { + + # Index it if it is not + unless (-e "$Args::args{'exclude_db'}-md5.idx") { + print +"Exclude Database has not been indexed - it will be as an md5sum file<br>\n"; + + print "<hr>\n"; + Hash::index_md5sum($Args::args{'exclude_db'}); + print "<hr>\n"; + } + + # only print it if it was successful + print HOST_CONFIG "exclude_db \'$Args::args{'exclude_db'}\'\n" + if (-e "$Args::args{'exclude_db'}-md5.idx"); + } + + close HOST_CONFIG; + + print "Configuration file (<tt>$fname</tt>) created<br><br>\n"; + + print "We must now import an image file for this host\n"; + + print "<br><br><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_OPEN_LOG&$Args::baseargs\">" + . "<img src=\"pict/menu_b_inew.jpg\" alt=\"Add Image\" " + . " height=20 border=\"0\"></a>\n"; + + Print::print_html_footer(); + return 0; +} + +# Open a host in the given case +sub host_open { + Print::print_html_header("Open Host In $Args::args{'case'}"); + + # Create an array of directories in the case, verifying that there is + # a config file + my @hosts; + opendir HOSTS, $::case_dir or die "Can't open $::case_dir directory: $!"; + foreach my $h (readdir HOSTS) { + next if (($h eq '.') || ($h eq '..')); + + my $hfile = Caseman::host_config_fname($h); + push @hosts, $h + if ((-d "$::case_dir" . "$h") && (-e "$hfile")); + } + closedir HOSTS; + + print "<b>Case:</b> $Args::args{'case'}<br><br>\n"; + + print "<center>"; + + if (scalar @hosts == 0) { + print "No hosts have been added to the case yet" + . "<br><br>Select the Add Host button below to create one.<br>\n"; + } + else { + + print "Select the host to open or create a new one<br>\n"; + + print_menu_tabs(); + + print "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\" " + . "background=\"$::YEL_PIX\" border=0>\n"; + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_OPEN_LOG\">\n" + . Args::make_hidden() + . "<tr><th>Name</th>" + . "<th>Description</th><th> </th></tr>\n"; + + my $first = 0; + foreach my $h (@hosts) { + + print "<tr><td align=\"left\">" + . "<input type=\"radio\" name=\"host\" value=$h"; + if ($first == 0) { + print " CHECKED"; + $first = 1; + } + print "> " . Print::html_encode($h) . " </td>"; + + my $fname = Caseman::host_config_fname($h); + open CONFIG, "<$fname" + or die "Can't open host config file ($fname)"; + + my $desc = "None Provided"; + while (<CONFIG>) { + s/^\s+//; + s/\s+$//; + + if (/^desc\s+(.*)$/) { + $desc = Print::html_encode($1); + last; + } + } + close CONFIG; + + print "<td align=left>$desc</td>" + . "<td align=center>" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::HOST_DETAILS&$Args::baseargs&" + . "host=$h\">details</a></td></tr>\n"; + } + print "</table>\n"; + + # Display pulldown of investigators + my @invs = read_invest(); + if (scalar @invs == 0) { + print "<input type=\"hidden\" name=\"inv\" value=\"unknown\">\n"; + } + else { + print "<br>Investigator (for reports only): "; + my $cur_inv = ""; + $cur_inv = $Args::args{'inv'} if (exists $Args::args{'inv'}); + + print "<select name=\"inv\" size=\"1\">\n"; + + if (($cur_inv eq "") && (scalar @invs != 1)) { + print "<option value=\"\" selected>Select One" . "</option>\n"; + } + foreach my $i (@invs) { + print "<option value=\"$i\""; + print " selected" if ($cur_inv eq $i); + print ">" . Print::html_encode($i) . "</option>\n"; + } + print "</select>\n"; + } + } + print "<br><br><table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr>\n"; + + # Make a table for the buttons. The table will either be 3 or 2 + # entries wide, depending on if there is an 'Ok' button or not + + unless (scalar @hosts == 0) { + print "<td align=center>" + . "<input type=\"image\" src=\"pict/menu_b_ok.jpg\" " + . "alt=\"Ok\" width=\"167\" height=20 border=0>\n" + . "</form>\n</td>\n"; + } + + # Add Host + print "<td align=center>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_ADD\">\n" + . "<input type=\"hidden\" name=\"case\" value=\"$Args::args{'case'}\">\n" + . "<input type=\"image\" src=\"pict/menu_b_hnew.jpg\" " + . "alt=\"Add Host\" width=\"176\" height=20 border=0>\n" + . "</form></td>" + . + + # Close Button + "<td align=center>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::CASE_OPEN\">\n" + . "<input type=\"image\" src=\"pict/menu_b_ccls.jpg\" " + . "alt=\"Close Case\" width=\"176\" height=20 border=0>\n" + . "</form></td></tr></table>\n"; + + print "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr><td> </td>" + . "<td align=center><a href=\"$::HELP_URL\" " + . " target=\"_blank\">" + . "<img src=\"pict/menu_b_help.jpg\" alt=\"Help\" " + . "width=\"167\" height=20 border=0>" + . "</a></td><td> </td></tr>\n" + . "</table>\n"; + + Print::print_html_footer(); + return 0; +} + +# Log that a given host was opened and then proceed to open an image +sub host_open_log { + unless ((exists $Args::args{'inv'}) && ($Args::args{'inv'} ne "")) { + my @invs = read_invest(); + if (scalar @invs == 0) { + $Args::args{'inv'} = $Args::enc_args{'inv'} = 'unknown'; + } + else { + Print::print_html_header("Missing Investigator"); + print "<br>An investigator must be selected<p>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_OPEN_LOG\">\n" + . Args::make_hidden(); + + print "Select one of the following:"; + print "<select name=\"inv\" size=\"1\">\n"; + + print "<option value=\"\" selected>Select One" . "</option>\n"; + + foreach my $i (@invs) { + print "<option value=\"$i\">$i</option>\n"; + } + print "</select><p>\n" + . "<input type=\"image\" src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=43 height=20 border=\"0\">\n" + . "</form>\n"; + + Print::print_html_footer(); + return 0; + } + } + + Args::check_inv(); + Print::log_case_info( + "Host $Args::args{'host'} opened by $Args::args{'inv'}"); + Print::log_host_info( + "Host $Args::args{'host'} opened by $Args::args{'inv'}"); + Print::log_host_inv("Host $Args::args{'host'} opened"); + + $Args::args{'view'} = $Args::enc_args{'view'} = $Caseman::VOL_OPEN; + vol_open(); +} + +# Provide details about the configuration of a host. This window is +# a link from the HOST_OPEN window +sub host_details { + Print::print_html_header( + "Details of $Args::args{'case'}:$Args::args{'host'}"); + + print "<b>Case: </b>$Args::args{'case'}<br><br>" + . "<center>" + . "<img src=\"pict/menu_h_hdet.jpg\" alt=\"Host Details\">" + . "<br><br><br>\n" + . "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\" " + . "background=\"$::YEL_PIX\" border=0>\n" + . + + # Name + "<tr><td align=\"right\" width=300><b>Name:</b></td>" + . "<td align=\"left\" width=300><tt>$Args::args{'host'}</tt></td></tr>\n" + . + + # Description + "<tr><td align=\"right\"><b>Description:</b></td>" + . "<td align=\"left\"><tt>" + . (($Caseman::host_desc ne "") ? $Caseman::host_desc : " ") + . "</tt></td></tr>\n" + . + + # Timezone + "<tr><td align=\"right\"><b>Time zone: </b></td>" + . "<td align=\"left\"><tt>$Caseman::tz</tt></td></tr>\n" + . + + # Timeskew + "<tr><td align=\"right\"><b>Timeskew:</b></td>" + . "<td align=\"left\"><tt>$Caseman::ts</tt></td></tr>\n" + . "<tr><td colspan=2> </td></tr>\n" + . + + # Actual Directory + "<tr><td align=\"right\"><b>Directory:</b></td>" + . "<td align=\"left\"><tt>" + . Print::html_encode($::host_dir) + . "</tt></td></tr>\n" + . "<tr><td colspan=2> </td></tr>\n" + . + + # Alert Database + "<tr><td align=\"right\"><b>Alert Hash Database:</b></td>" + . "<td align=\"left\"><tt>" + . (($Caseman::alert_db ne "") + ? Print::html_encode($Caseman::alert_db) + : " ") + . "</tt></td></tr>\n" + . + + # Exclude Database + "<tr><td align=\"right\"><b>Exclude Hash Database:</b></td>" + . "<td align=\"left\"><tt>" + . (($Caseman::exclude_db ne "") + ? Print::html_encode($Caseman::exclude_db) + : " ") + . "</tt></td></tr>\n" + . "</table>\n"; + + # Final Button + print "<br><br><form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_OPEN\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/menu_b_ok.jpg\" " + . "alt=\"Ok\" width=\"167\" height=20 border=\"0\">\n</form>"; + + Print::print_html_footer(); + + return; +} + +# Read the investigators file and return a sorted list +sub read_invest { + my $fname = investig_fname(); + open INVES, "<$fname" or return; + + my @investigs; + while (<INVES>) { + chomp; + s/^\s+//; + s/\s+$//; + push @investigs, $1 + if (/^($::REG_INVESTIG)$/o); + } + close(INVES); + sort { lc($a) cmp lc($b) } @investigs; +} + +# File of investigators name in list +sub investig_fname { + return "$::case_dir" . "investigators.txt"; +} + +#################################################################### +# Image Functions + +# Types of modes for fname (i.e. can we overwrite it if it exists) +my $FNAME_MODE_INIT = 0; +my $FNAME_MODE_OVER = 1; + +my $MD5_NOTHING = 1; +my $MD5_CALC = 2; +my $MD5_ADD = 3; + +my $IMG_ADD_SYM = 1; +my $IMG_ADD_COPY = 2; +my $IMG_ADD_MOVE = 3; + +# Open an image that has been configured +sub vol_open { + Print::print_html_header( + "Open Image In $Args::args{'case'}:$Args::args{'host'}"); + + print "<b>Case:</b> $Args::args{'case'}<br>\n"; + print "<b>Host:</b> $Args::args{'host'}<br>\n"; + print "<center>\n"; + + # the images have been loaded from reading the host config file in + # autopsy_main + if (scalar(keys %Caseman::vol2ftype) == 0) { + print "No images have been added to this host yet<br><br>\n" + . "Select the Add Image File button below to add one\n"; + goto EGRESS; + } + + if ($::LIVE == 1) { + print "Select a volume to analyze.<br>\n"; + } + else { + print "Select a volume to analyze or add a new image file.<br>\n"; + } + + print_menu_tabs(); + + print "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\" " + . "background=\"$::YEL_PIX\" border=0>\n"; + + # We want to sort, so rearrange the hash + my %mnt2vol; + my %par2disk; + + # Cycle through each image we read from the host config + foreach my $i (keys %Caseman::vol2cat) { + if ($Caseman::vol2cat{$i} eq "disk") { + $mnt2vol{"1disk--AUTOPSY--$i"} = $i; + } + elsif ($Caseman::vol2cat{$i} eq "part") { + if ( ($Caseman::vol2ftype{$i} eq "raw") + || ($Caseman::vol2ftype{$i} eq "swap")) + { + $mnt2vol{"2$Caseman::vol2ftype{$i}--AUTOPSY--$i"} = $i; + } + else { + $mnt2vol{"2$Caseman::vol2mnt{$i}--AUTOPSY--$i"} = $i; + } + } + } + + # sort via parent volume, then starting location, + # and then mount point (which includes the name) + my @mnt = sort { + ($Caseman::vol2par{$mnt2vol{$a}} cmp $Caseman::vol2par{$mnt2vol{$b}}) + or ($Caseman::vol2start{$mnt2vol{$a}} <=> + $Caseman::vol2start{$mnt2vol{$b}}) + or (lc($a) cmp lc($b)) + } keys %mnt2vol; + + # It is possible to have only the blkls image and not the original + # We need to search for those now because they will not be in the + # list that we just made (which are arranged via mount point) + my @orphan_blkls; + + # cycle through each image and check its type and original + foreach my $k (keys %Caseman::vol2ftype) { + if ( ($Caseman::vol2ftype{$k} eq "blkls") + && (!exists $Caseman::mod2vol{$k})) + { + push @orphan_blkls, $k; + } + } + + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_OPEN_LOG\">\n" + . Args::make_hidden() + + . "<tr><th> </th>" + . "<th align=left>mount</th>" + . "<th align=left>name</th>" + . # vol name + "<th align=left>fs type</th></tr>\n"; + + my $prev_par = ""; + + for (my $i = 0; $i <= $#mnt; $i++) { + my $vol = $mnt2vol{$mnt[$i]}; + + if ($Caseman::vol2par{$vol} ne $prev_par) { + print "<tr><td colspan=5><hr></td></tr>\n" if ($i != 0); + $prev_par = $Caseman::vol2par{$vol}; + } + + # Mount Point + # If we have the dummy string at the end of the duplicate + # entry, then take it off and print the original + $mnt[$i] = $1 if ($mnt[$i] =~ /^\d(.*?)--AUTOPSY--$::REG_VNAME$/o); + print "<tr>" . "<td><input type=\"radio\" name=\"vol\" value=$vol"; + print " CHECKED" if ($i == 0); + print "></td>" + . "<td><tt>" + . Print::html_encode($mnt[$i]) + . "</tt></td>"; + + # image name and ftype + print +"<td><tt>$Caseman::vol2sname{$vol}</tt></td><td>$Caseman::vol2ftype{$vol}</td>"; + if ($::LIVE == 0) { + print "<td align=center><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_DETAILS&$Args::baseargs&" + . "vol=$vol\">details</a></td>" + . "</tr>\n"; + } + else { + print "<td> </td></tr>\n"; + } + } + + # If we are done with the regular images and have some orphan + # blkls images, print them + my @sort = sort @orphan_blkls; + for (my $i = 0; $i <= $#sort; $i++) { + print +"<tr><td> </td><td> </td><td>(<input type=\"radio\" name=\"vol\" " + . "value=$sort[$i]"; + print " CHECKED" if ($#mnt == 0); + print "> unalloc)</td><td><tt>" + . Print::html_encode($Caseman::vol2sname{$sort[$i]}) + . "</tt></td><td>" + . Print::html_encode($Caseman::vol2ftype{$sort[$i]}) + . "</td></tr>\n"; + } + + # Begin Button + print "</table>\n"; + + EGRESS: + + print "<br><br>" + . "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n"; + + # Ok Button + if (scalar(keys %Caseman::vol2ftype) == 0) { + print "<tr><td width=200> </td>\n"; + } + else { + print "<tr><td align=center width=200>" + . "<input type=\"image\" src=\"pict/menu_b_analyze.jpg\" " + . "alt=\"Analyze\" width=\"167\" height=20 border=0>\n" + . "</form></td>\n"; + } + + # Image Add Button + if ($::LIVE == 0) { + print "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::IMG_ADD\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/menu_b_ifnew.jpg\" " + . "alt=\"Add Image\" width=167 height=20 border=0></form></td>\n" + . + + # Cancel Button + "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::HOST_OPEN\">\n" + . "<input type=\"hidden\" name=\"case\" value=\"$Args::args{'case'}\">\n" + . "<input type=\"image\" src=\"pict/menu_b_hcls.jpg\" " + . "width=167 height=20 alt=\"Close Host\" border=0>\n" + . "</form></td></tr>"; + } + else { + print "<td width=200> </td><td width=200> </td></tr>\n"; + } + + # Help Button + print +"<td width=200> </td><td align=center width=200><a href=\"$::HELP_URL\" " + . " target=\"_blank\">" + . "<img src=\"pict/menu_b_help.jpg\" alt=\"Help\" " + . "width=\"167\" height=20 border=0>" + . "</a></td><td width=200> </td></tr>\n" + . "</table>\n"; + + # Other features that can be done on a host + + if ($::LIVE == 0) { + print "<hr><p>" + . "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr>\n"; + + # Timeline of file activity + print "<td align=\"center\" width=200>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&" + . "mod=$::MOD_TL&view=$Timeline::FRAME\">" + . "<img border=0 " + . "src=\"pict/menu_b_tl.jpg\" " + . "width=\"167\" height=20 " + . "alt=\"File Activity Timelines\"></a></td>\n"; + + # verify the integrity of the images + print "<td align=\"center\" width=200>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&" + . "mod=$::MOD_HASH&view=$Hash::IMG_LIST_FR\">" + . "<img border=0 " + . "src=\"pict/menu_b_int.jpg\" " + . "width=\"167\" height=20 " + . "alt=\"Image Integrity\"></a></td>\n" + . + + # Hashdatabases + "<td align=\"center\" width=200>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&" + . "mod=$::MOD_HASH&view=$Hash::DB_MANAGER\">" + . "<img border=0 " + . "src=\"pict/menu_b_hashdb.jpg\" " + . "width=\"167\" height=20 " + . "alt=\"Hash Databases\"></a></td>\n" + . "</tr></table>\n"; + + # Notes + if ($::USE_NOTES == 1) { + print "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr>\n" + . "<td align=\"center\" width=300>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_NORM\">" + . "<img border=0 " + . "src=\"pict/menu_b_note.jpg\" " + . "width=\"167\" height=20 " + . "alt=\"View Notes\"></a></td>\n" + . + + "<td width=300 align=\"center\">" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_SEQ\">" + . "<img border=0 " + . "src=\"pict/menu_b_seq.jpg\" " + . "width=\"167\" height=20 " + . "alt=\"Event Sequencer\"></a></td>\n" + . + + "</tr>\n" . "</table>\n"; + } + + # If LIVE + } + else { + print "<a href=\"./about\"><br>\n" + . "<img src=\"pict/logo.jpg\" border=0 alt=\"Logo\"></a><br>\n"; + + } + + Print::print_html_footer(); + + return 0; +} + +# Log in the host log that a given image was opened by what user +# then open the main window +sub vol_open_log { + + # These will be stopped in the func during LIVE + Print::log_host_info( + "Image $Args::args{'vol'} opened by $Args::args{'inv'}"); + Print::log_host_inv("$Args::args{'vol'}: volume opened"); + + $Args::args{'mod'} = $Args::enc_args{'mod'} = $::MOD_FRAME; + $Args::args{'view'} = $Args::enc_args{'view'} = $Frame::IMG_FRAME; + Frame::main(); +} + +# Menu to add a new image to the host +# +# The list of new images is determined by looking in the images directory +# and seeing which is not yet configured +# +sub img_add { + Print::print_html_header( + "Add Image To $Args::args{'case'}:$Args::args{'host'}"); + + print "<b>Case:</b> $Args::args{'case'}<br>\n" + . "<b>Host:</b> $Args::args{'host'}<br>\n"; + + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::IMG_ADD_PREP\">\n" + . Args::make_hidden(); + + print <<EOF1; +<center> +<img src=\"pict/menu_h_inew.jpg\" alt=\"Add Image\"> +<br><br><br> + +<table width=\"600\" cellpadding=\"2\" cellspacing=\"0\" background=\"$::YEL_PIX\" border=0> +<tr> + <td colspan=4> </td> +</tr> + +<tr> + <td align=left colspan=4> + 1. <b>Location</b><br>Enter the full path (starting with <tt>/</tt>) to the image file.<br> + If the image is split (either raw or EnCase), then enter '*' for the extension. + </td> +</tr> +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"text\" name=\"img_path\" size=36 maxlength=256> + </td> +</tr> +<tr><td colspan=4> </td><tr> +<tr> + <td align=left colspan=4>2. <b>Type</b><br>Please select if this image file is for a disk or a single partition.</td> +</tr> +<tr> + <td> </td> + <td align=left> + <input type=\"radio\" name=\"imgtype\" value="disk" CHECKED> + Disk + </td> + <td align=left> + <input type=\"radio\" name=\"imgtype\" value="volume"> + Partition + </td> + <td align=left> + </td> +</tr> + +<tr><td colspan=4> </td><tr> +<tr> + <td align=left colspan=4>3. <b>Import Method</b><br>To analyze the image file, it must be located in the evidence locker. It can be imported from its current location using a symbolic link, by copying it, or by moving it. Note that if a system failure occurs during the move, then the image could become corrupt.</td> +</tr> +<tr> + <td> </td> + <td align=left> + <input type=\"radio\" name=\"sort\" value=$IMG_ADD_SYM CHECKED> + Symlink + </td> + <td align=left> + <input type=\"radio\" name=\"sort\" value=$IMG_ADD_COPY> + Copy + </td> + <td align=left> + <input type=\"radio\" name=\"sort\" value=$IMG_ADD_MOVE> + Move + </td> +</tr> + + +<tr> + <td colspan=4> </td> +</tr> + +</table> + +<br> +<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center colspan=2> + <input type=\"image\" src=\"pict/menu_b_next.jpg\" alt=\"Next Step\" width=\"167\" height=20 border=0> + </td> +</tr> + +</form> + +EOF1 + + print "<tr><td colspan=2> </td></tr>\n" + . "<td align=center>\n" + . " <form action=\"$::PROGNAME\" method=\"get\">\n" + . " <input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . " <input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_OPEN\">\n" + . + + Args::make_hidden() + . " <input type=\"image\" src=\"pict/menu_b_cancel.jpg\" " + . "alt=\"Cancel\" width=\"167\" height=20 border=0></form>\n" + . " </td>\n" + . + + # HELP + " <td align=center>\n" + . " <a href=\"$::HELP_URL\" target=\"_blank\">\n" + . " <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" " + . "width=\"167\" height=20 border=0></a>\n" + . " </td>\n" + . "</tr>\n" + . "</table>\n"; + + Print::print_html_footer(); + + return 0; +} + +# List the images from the glob - called from img_add_prep if spl_conf is not set +sub img_add_split_conf { + my $img_path = Args::get_img_path_wild(); + my $img_type = $Args::args{'imgtype'}; + + print "<center><br><br><b>Split Image Confirmation</b><br><br>\n"; + + my @spl_img = glob($img_path); + if (scalar(@spl_img) == 0) { + print "No images were found at this location ($img_path)<br>\n" + . "Use the back button and fix the path<br>\n"; + return; + } + + print <<EOF1; +The following images will be added to the case.<br> +If this is not the correct order, then you should change the naming convention.<br> +Press the Next button at the bottom of the page if this is correct.<br><br> + +<table width=\"600\" cellpadding=\"2\" cellspacing=\"0\" background=\"$::YEL_PIX\" border=0> +<tr> + <td colspan=2> </td> +</tr> +EOF1 + + my $a = 0; + foreach $i (@spl_img) { + + # We need to do this when we analyze the image, so do it here too to show + # what will be analyzed + $i = $1 if ($i =~ /^($::REG_IMG_PATH)$/); + print +"<tr><td align=\"center\">$a</td><td align=\"left\"><tt>$i</tt></td></tr>\n"; + $a++; + } + + my $vs = ""; + $vs = "&vstype=$Args::args{'vstype'}" + if (exists $Args::args{'vstype'}); + + # Print the Ok Button + print "</table><br><br>\n" + . "<table width=\"600\" cellpadding=\"2\" cellspacing=\"0\" border=0>" + . "<tr><td width=\"300\" align=\"center\"><a href=\"$::PROGNAME?${Args::baseargs_novol}&" + . "mod=$::MOD_CASEMAN&view=$Caseman::IMG_ADD_PREP&spl_conf=1&sort=$Args::args{'sort'}&" + . "img_path=$img_path&imgtype=$Args::args{'imgtype'}$vs\">" + . "<img src=\"pict/menu_b_next.jpg\" alt=\"Next\" " + . "width=\"167\" height=20 border=0></a></td>\n" + . "<td width=\"300\" align=\"center\"><a href=\"$::PROGNAME?${Args::baseargs_novol}&" + . "mod=$::MOD_CASEMAN&view=$Caseman::IMG_ADD\">" + . "<img src=\"pict/menu_b_cancel.jpg\" alt=\"cancel\" width=\"167\" height=\"20\" border=\"0\">" + . "</a></td></tr></table>\n"; + + return 0; +} + +# Run the autodetect stuff and get confirmation from the user +sub img_add_prep { + Args::check_img_path_wild(); + Args::check_sort(); + unless ((exists $Args::args{'imgtype'}) + && ($Args::args{'imgtype'} =~ /^\w+$/)) + { + Print::print_check_err("Invalid image type"); + } + + Print::print_html_header("Collecting details on new image file"); + + my $img_path = Args::get_img_path_wild(); + + my $img_type = $Args::args{'imgtype'}; + my $spl_conf = 0; + $spl_conf = 1 + if ((exists $Args::args{'spl_conf'}) && ($Args::args{'spl_conf'} == 1)); + + # If we have a wildcard then it is a split image, so we verify it first and + # then make a string of the images so we can test it. + if ($img_path =~ /[\*\?]/) { + if ($spl_conf == 0) { + return img_add_split_conf(); + } + else { + $img_tmp = ""; + foreach my $i (glob($img_path)) { + if ($i =~ /^($::REG_IMG_PATH)$/) { + $img_tmp .= "\"$1\" "; + } + } + $img_path = $img_tmp; + } + } + else { + unless ((-f $img_path) + || (-d $img_path) + || (-l $img_path) + || (-b $img_path) + || (-c $img_path)) + { + Print::print_err("Image file not found ($img_path)"); + } + $img_path = "\"$img_path\""; + } + + # Get the image type + local *OUT; + Exec::exec_pipe(*OUT, "'$::TSKDIR/img_stat' -t $img_path"); + my $itype = Exec::read_pipe_line(*OUT); + if (defined $itype) { + chomp $itype; + $itype = $1 if ($itype =~ /^(\w+)$/); + } + else { + print +"The image format type could not be determined for this image file<br>\n"; + return; + } + close(OUT); + + # The plan here is to collect the needed info and then we print it + + my $conflict = 0; + my $cnt = 0; + $start[0] = ""; + $end[0] = ""; + $type[0] = ""; + $desc[0] = ""; + $active[0] = ""; + + my $vstype = ""; + + my $vstype_flag = ""; + my $mmls_out = ""; # Will contain output of mmls (if disk image) + if ($img_type eq "disk") { + my $out; + + if ( (exists $Args::args{'vstype'}) + && ($Args::args{'vstype'} =~ /^(\w+)$/)) + { + $vstype = $Args::args{'vstype'}; + $vstype_flag = "-t $vstype"; + } + + # Get the type + else { + + Exec::exec_pipe(*OUT, "'$::TSKDIR/mmstat' -i $itype $img_path"); + + $vstype = Exec::read_pipe_line(*OUT); + close(OUT); + + chomp $vstype if (defined $vstype); + + if ( (!defined $vstype) + || ($vstype =~ /^Error determining/) + || ($vstype eq "") + || (!exists $Vs::type{$vstype}) + || ($vstype !~ /^\w+$/)) + { + print +"<table><tr><td colspan=2><font color=\"$::DEL_COLOR[0]\">Warning:</font> Autopsy could not determine the volume system type for the disk image (i.e. the type of partition table).<br>\n" + . "Please select the type from the list below or reclassify the image as a volume image instead of as a disk image.</td></tr>\n" + . "<tr><td colspan=2> </td></tr>\n" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::IMG_ADD_PREP\">\n" + . "<input type=\"hidden\" name=\"spl_conf\" value=\"1\">\n" + . "<input type=\"hidden\" name=\"img_path\" value=\"$Args::args{'img_path'}\">\n" + . "<input type=\"hidden\" name=\"sort\" value=\"$Args::enc_args{'sort'}\">\n" + . Args::make_hidden() + . "<tr><td>Disk Image <input type=\"radio\" name=\"imgtype\" value=\"disk\" CHECKED></td>\n" + . "<td>Volume Image<input type=\"radio\" name=\"imgtype\" value=\"volume\"></td></tr>\n" + . "<tr><td>Volume System Type (disk image only): <select name=\"vstype\">\n"; + + foreach my $vs (sort keys %Vs::type) { + print "<option value=\"$vs\""; + print " selected" if ($vs eq 'dos'); + print ">${vs}</option>\n"; + } + + print "</select></td>" + . "<td> </td></tr>" + . "<tr><td colspan=2> </td></tr>" + . "</table><br><br>\n" + . "<input type=\"image\" src=\"pict/menu_b_ok.jpg\" alt=\"Ok\" width=\"176\" height=20 border=0>" + . "</form>\n"; + return; + } + $vstype = $1 if ($vstype =~ /^(\w+)$/); + $vstype_flag = "-t $vstype"; + } + + # Run 'mmls' on the image + Exec::exec_pipe(*OUT, + "'$::TSKDIR/mmls' -a -i $itype -aM $vstype_flag -r $img_path"); + + # cycle through results and add each to table with file system type + my $part_type = ""; + + while ($_ = Exec::read_pipe_line(*OUT)) { + $mmls_out .= "$_"; # Save the line + last if (/^Error determining partition/); + + if (/^\d+:\s+[\d:]+\s+(\d+)\s+(\d+)\s+\d+\s+(\S.*)$/) { + $start[$cnt] = $1; + $end[$cnt] = $2; + $desc[$cnt] = $3; + $active[$cnt] = 1; + } + elsif ((/^DOS Partition/) + || (/^BSD Disk/) + || (/^Sun VTOC/) + || (/^MAC Partition/) + || (/^GUID Partition/)) + { + $part_type = $_; + + #print "<tr><td colspan=7> </td></tr>\n"; + #print "<tr><td colspan=7>$_</td></tr>\n"; + next; + } + elsif (/^Sector:/) { + + #print "<tr><td colspan=7>$_</td></tr>\n"; + next; + } + else { + next; + } + + # Skip the BSD partition for the full disk + next + if ( ($part_type =~ /^BSD Disk/) + && ($start[$cnt] == 0) + && ($desc[$cnt] =~ /^Unused/)); + + # Skip if this is an extended DOS partition + next + if ( ($part_type =~ /^DOS Partition/) + && ($desc[$cnt] =~ /Extended \(/)); + + # Get rid of the leading 0s + $start[$cnt] = $1 + if ($start[$cnt] =~ /^0+([1-9]\d*)$/); + $end[$cnt] = $1 + if ($end[$cnt] =~ /^0+([1-9]\d*)$/); + + # Do we already have this partition? + my $i; + for ($i = 0; $i < $cnt; $i++) { + next if ($active[$i] == 0); + + if ($start[$i] == $start[$cnt]) { + $conflict = 1; + + if ($end[$i] == $end[$cnt]) { + last; + } + + #The previous was the BSD partition - skip it */ + if ( ($desc[$i] =~ /^FreeBSD \(0xA5\)/) + || ($desc[$i] =~ /^OpenBSD \(0xA6\)/) + || ($desc[$i] =~ /^NetBSD \(0xA9\)/)) + { + $active[$i] = 0; + + # if the current one is the BSD partition for + # the full partition/disk then skip it + if ($desc[$cnt] =~ /^Unused /) { + $active[$cnt] = 0; + } + } + } + + # Do we start inside of another? + if (($start[$i] > $start[$cnt]) && ($end[$i] < $start[$cnt])) { + $conflict = 1; + } + + # Do we end inside of another? + elsif (($start[$i] < $end[$cnt]) && ($end[$i] > $end[$cnt])) { + $conflict = 1; + } + } + if (($end[$i] == $end[$cnt]) && ($i != $cnt)) { + next; + } + + local *OUT2; + my $out2; + + # Run 'fstat -t' on the image + Exec::exec_pipe(*OUT2, + "'$::TSKDIR/fsstat' -o $start[$cnt] -i $itype -t $img_path"); + + $type[$cnt] = Exec::read_pipe_line(*OUT2); + close(OUT2); + + if ( (!exists $type[$cnt]) + || (!defined $type[$cnt]) + || ($type[$cnt] =~ /^Cannot determine/) + || ($type[$cnt] eq "")) + { + $type[$cnt] = "Unknown"; + } + chomp $type[$cnt]; + + $cnt++; + } + close(OUT); + + if ($conflict == 1) { + print +"<tr><td colspan=2><font color=\"$::DEL_COLOR[0]\">Warning:</font> Conflicts in the partitions were detected.<br>The full <tt>mmls</tt> output is given at the bottom of the page</td></tr>\n" + . "<tr><td colspan=2> </td></tr>\n"; + } + } + + # If a volume, then run fsstat on it + elsif ($img_type eq "volume") { + + # Run 'fstat -t' on the image + Exec::exec_pipe(*OUT, "'$::TSKDIR/fsstat' -t -i $itype $img_path"); + + $type[0] = Exec::read_pipe_line(*OUT); + close(OUT); + + if ( (!defined $type[0]) + || ($type[0] =~ /^Cannot determine/) + || ($type[0] eq "")) + { + $type[0] = "Unknown"; + print +"<font color=\"$::DEL_COLOR[0]\">Warning:</font> The file system type of the volume image file could not be determined.<br>\n" + . "If this is a disk image file, return to the previous page and change the type.<br><br>\n"; + } + chomp $type[0]; + $start[0] = 0; + $end[0] = 0; + $active[0] = 1; + $desc[0] = $type[0]; + $cnt++; + close(OUT); + } + else { + Print::print_err("Unknown image type: $img_type"); + } + + my $sname = $img_path; + $sname = "$::IMGDIR/" . "$1" if ($sname =~ /\/($::REG_FILE)\"$/); + +# Now that we have the information about the partitions and disks, print the fields + print <<EOF1; + +<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\"> +<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\"> +<input type=\"hidden\" name=\"view\" value=\"$Caseman::IMG_ADD_DOIT\"> +<input type=\"hidden\" name=\"img_path\" value=\"$Args::args{'img_path'}\"> +<input type=\"hidden\" name=\"num_img\" value=\"$cnt\"> +<input type=\"hidden\" name=\"sort\" value=\"$Args::enc_args{'sort'}\"> + + +<center> +<h3>Image File Details</h3> +<table width=\"600\" cellpadding=\"2\" cellspacing=\"0\" background=\"$::YEL_PIX\" border=0> +<tr> + <td align=left colspan=4> + <b>Local Name: </b> $sname + </td> +</tr> + +EOF1 + + # We do not currently offer integrity options for non-raw files + if (($itype eq "raw") || ($itype eq "split")) { + + print <<EOF1b; +<tr> + <td align=left colspan=4> + <b>Data Integrity: </b> An MD5 hash can be used to verify the + integrity of the image. (With split images, this hash is for the full image file) + </td> +</tr> + +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"radio\" name=\"do_md5\" value=\"$MD5_NOTHING\" CHECKED> + <u>Ignore</u> the hash value for this image. + </td> +</tr> + +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"radio\" name=\"do_md5\" value=\"$MD5_CALC\"> + <u>Calculate</u> the hash value for this image. + </td> +</tr> + +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"radio\" name=\"do_md5\" value=\"$MD5_ADD\"> + <u>Add</u> the following MD5 hash value for this image: + </td> +</tr> + +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"text\" name=\"md5\" size=36 maxlength=32> + </td> +</tr> + +<tr> + <td> </td> + <td align=left colspan=3> + <input type=\"checkbox\" name=\"ver_md5\" value=\"1\"> + Verify hash after importing? + </td> +</tr> +EOF1b + } + else { + print + "<input type=\"hidden\" name=\"do_md5\" value=\"$MD5_NOTHING\">\n"; + } + + print <<EOF1c; +</table> + +<h3>File System Details</h3> +<table width=\"600\" cellpadding=\"2\" cellspacing=\"0\" background=\"$::YEL_PIX\" border=0> + +<tr> + <td colspan=2 align=left>Analysis of the image file shows the following partitions:</td> +</tr> +<tr> + <td colspan=2> </td> +</tr> + +EOF1c + + print Args::make_hidden(); + + print "<input type=\"hidden\" name=\"vstype\" value=\"$vstype\">\n" + if ($vstype ne ""); + + my $idx = 1; + my $ms_cnt = 0; + my @ms_name = ("C:", "D:", "E:", "F:", "G:", "H:", "I:", "J:"); + for (my $i = 0; $i < $cnt; $i++) { + next if ($active[$i] == 0); + print +"<tr><td colspan=2><u>Partition $idx</u> (Type: $desc[$i])</td><tr>\n"; + + if ($cnt > 1) { + print "<tr><td colspan=2> Add to case? " + . "<input type=\"checkbox\" name=\"yes-${idx}\" value=1 CHECKED></td></tr>\n"; + } + else { + print "<input type=\"hidden\" name=\"yes-${idx}\" value=1>\n"; + } + + unless (($start[$i] == 0) && ($end[$i] == 0)) { + print "<tr><td colspan=2> Sector Range: " + . "$start[$i] to $end[$i]" + . "</td></tr>\n"; + } + + print + "<input type=\"hidden\" name=\"start-${idx}\" value=\"$start[$i]\">" + . "<input type=\"hidden\" name=\"end-${idx}\" value=\"$end[$i]\">\n" + . "<tr><td> Mount Point: <input type=\"text\" name=\"mnt-${idx}\" size=\"6\""; + if (($type[$i] =~ /^ntfs/) || ($type[$i] =~ /^fat/)) { + print " value=\"$ms_name[$ms_cnt]\"" + if ($ms_cnt < 8); + $ms_cnt++; + } + elsif (($type[$i] =~ /^raw/) + || ($type[$i] =~ /^swap/)) + { + print " value=\"N/A\""; + } + else { + print " value=\"/$idx/\""; + } + print "></td>\n" + . "<td>File System Type: <select name=\"ftype-${idx}\">\n"; + + foreach my $fs (@Fs::types) { + print "<option value=\"$fs\""; + print " selected" if ($fs eq $type[$i]); + print ">${fs}</option>\n"; + } + + # The following do not have 'metas' but should be in the list + print "<option value=\"\">======</option>\n"; + if ($type[$i] eq "Unknown") { + print "<option value=\"raw\" selected>raw</option>\n"; + } + else { + print "<option value=\"raw\">raw</option>\n"; + } + + print "<option value=\"swap\">swap</option>\n" + . "</select></td></tr>\n" + . "<tr><td colspan=2> </td></tr>\n"; + + $idx++; + } + + print "</table>\n"; + + print <<EOF2; +<br><br> +<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center> + <input type=\"image\" src=\"pict/menu_b_add.jpg\" + alt=\"Add\" width=\"176\" height=20 border=0> + </td> +</form> + <td align=center> + <form action=\"$::PROGNAME\" method=\"get\"> +EOF2 + print Args::make_hidden(); + print <<EOF3; + <input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\"> + <input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_OPEN\"> + <input type=\"image\" src=\"pict/menu_b_cancel.jpg\" + alt=\"Cancel\" width=\"167\" height=20 border=0> + </form> + </td> + <td align=center><a href=\"$::HELP_URL\" + target=\"_blank\"> + <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" + width=\"167\" height=20 border=0></a> + </td> +</tr> +</table> +EOF3 + + if ($img_type eq "disk") { + print +"</center><p>For your reference, the <tt>mmls</tt> output was the following:<br><pre>$mmls_out</pre>\n"; + } + + Print::print_html_footer(); + + return 0; +} + +# Add the image to the configuration by adding it to the host config file +# and the md5.txt file if that data was provided +sub img_add_doit { + + Args::check_num_img(); + Args::check_img_path_wild(); + Args::check_sort(); + Args::check_do_md5(); + + my $num_img = Args::get_num_img(); + my $img_path = Args::get_img_path_wild(); + my $import_type = Args::get_sort(); + + Print::print_html_header("Add a new image to an Autopsy Case"); + + my $err = 0; + my $add_num = 0; + $start[0] = 0; + $end[0] = 0; + $ftype[0] = ""; + $mnt[0] = ""; + + # We need a string with all images in it for the hashes and file system testing + my $img_path_full; + if ($img_path =~ /[\*\?]/) { + $img_path_full = ""; + foreach my $i (glob($img_path)) { + if ($i =~ /^($::REG_IMG_PATH)$/) { + $img_path_full .= "\"$1\" "; + } + } + } + else { + $img_path_full = "\"$img_path\""; + } + + # Get the image type + local *OUT; + Exec::exec_pipe(*OUT, "'$::TSKDIR/img_stat' -t $img_path_full"); + my $itype = Exec::read_pipe_line(*OUT); + if (defined $itype) { + chomp $itype; + $itype = $1 if ($itype =~ /^(\w+)$/); + } + else { + print +"The image format type could not be determined for this image file<br>\n"; + return; + } + close(OUT); + + # Check the hash of the image if that is the plan + my $do_md5 = Args::get_do_md5(); + my $act_md5 = ""; + unless ($do_md5 == $MD5_NOTHING) { + + # Do we need to calculate an MD5? + if ( + ($do_md5 == $MD5_CALC) + || ( ($do_md5 == $MD5_ADD) + && (exists $Args::args{'ver_md5'}) + && ($Args::args{'ver_md5'} == 1)) + ) + { + + print "<p>Calculating MD5 (this could take a while)<br>\n"; + $act_md5 = Hash::calc_md5_split($img_path_full); + unless ($act_md5 =~ /^$::REG_MD5$/o) { + print "Error calculating MD5: $act_md5<br>\n"; + return 1; + } + print "Current MD5: <tt>$act_md5</tt><br>\n"; + } + + # And md5 value was given so we can add it to the md5.txt file + if (($do_md5 == $MD5_ADD) && (exists $Args::args{'md5'})) { + + my $md5 = $Args::args{'md5'}; + unless ($md5 =~ /^$::REG_MD5$/o) { + if ($md5 eq "") { + print "MD5 value missing<br>\n"; + } + else { + print "Invalid MD5 value (32 numbers or letters a-f)<br>\n"; + } + print "<p><a href=\"$::PROGNAME?" + . "mod=$::MOD_CASEMAN&view=$Caseman::IMG_ADD&" + . "$Args::baseargs\">" + . "<img src=\"pict/menu_b_back.jpg\" border=\"0\" " + . "width=\"167\" height=20 alt=\"Back\"></a>\n"; + return 1; + } + $md5 =~ tr/[a-f]/[A-F]/; + + # They also want us to validate the MD5 + if ( (exists $Args::args{'ver_md5'}) + && ($Args::args{'ver_md5'} == 1)) + { + + if ($act_md5 eq $md5) { + print "Integrity Check Passed<br>\n"; + Print::log_host_info("Integrity check passed on new image"); + } + else { + print "<font color=\"$::DEL_COLOR[0]\">" + . "Integrity Check Failed<br></font><br>\n" + . "Provided: <tt>$md5</tt><br>\n" + . "Image not added to case<br>\n"; + + Print::log_host_info("Integrity check failed on new image"); + return 1; + } + } + + # set the act_md5 value to what was given and verified + $act_md5 = $md5; + } + + # We will add the MD5 to the config file after we get its ID + } + + # Proces the image arguments to make sure they are all there and test the + # file system type + print "Testing partitions<br>\n"; + for (my $i = 0; $i <= $num_img; $i++) { + + next + unless ((exists $Args::args{"yes-" . $i}) + && ($Args::args{"yes-" . $i} == 1)); + + if ( (exists $Args::args{"start-" . $i}) + && ($Args::args{"start-" . $i} =~ /^(\d+)$/)) + { + $start[$add_num] = $1; + } + else { + print "Missing starting address for partition $i<br>\n"; + $err = 1; + last; + } + + if ( (exists $Args::args{"end-" . $i}) + && ($Args::args{"end-" . $i} =~ /^(\d+)$/)) + { + $end[$add_num] = $1; + } + else { + print "Missing ending address for partition $i<br>\n"; + $err = 1; + last; + } + + if ( (exists $Args::args{"mnt-" . $i}) + && ($Args::args{"mnt-" . $i} =~ /^($::REG_MNT)$/)) + { + $mnt[$add_num] = $1; + } + else { + print "Missing mount point for partition $i<br>\n"; + $err = 1; + last; + } + + if ( (exists $Args::args{"ftype-" . $i}) + && ($Args::args{"ftype-" . $i} =~ /^($::REG_FTYPE)$/)) + { + $ftype[$add_num] = $1; + } + else { + print "Missing file system type for partition $i<br>\n"; + $err = 1; + last; + } + + # Test the File System + if (($ftype[$add_num] ne 'swap') && ($ftype[$add_num] ne 'raw')) { + + local *OUT; + my $out; + + # Run 'fsstat' and see if there is any output - else there was + # an error and the data went to STDERR + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fsstat' -o $start[$add_num] -i $itype -f $ftype[$add_num] $img_path_full" + ); + unless (read(OUT, $out, 1)) { + print +"<p>Partition $i is not a <tt>$ftype[$add_num]</tt> file system<br>\n"; + $err = 1; + last; + } + close(OUT); + } + $add_num++; + } + + # Go back if we got an error + if ($err == 1) { + print "Use the browser's back button to fix the data<br>\n"; + return 1; + } + + ################################################## + # Copy the images and add them to the config file + + if ($import_type == $IMG_ADD_SYM) { + Print::print_err("ERROR: /bin/ln missing") + unless (-x '/bin/ln'); + + print "Linking image(s) into evidence locker<br>\n"; + } + elsif ($import_type == $IMG_ADD_COPY) { + Print::print_err("ERROR: /bin/cp missing") + unless (-x '/bin/cp'); + + print +"Copying image(s) into evidence locker (this could take a little while)<br>\n"; + } + elsif ($import_type == $IMG_ADD_MOVE) { + Print::print_err("ERROR: /bin/mv missing") + unless (-x '/bin/mv'); + + print "Moving image(s) into evidence locker<br>\n"; + } + else { + Print::print_err("Invalid Import Type: $import_type\n"); + } + + my $imgid = ""; + foreach my $i (glob($img_path)) { + + # remove the tainting + $i = $1 if ($i =~ /^($::REG_IMG_PATH)$/); + + # Deterine the local (target) name + my $img = ""; + if ($i =~ /\/($::REG_FILE)$/) { + $img = "$::IMGDIR/$1"; + } + else { + Print::print_err("Error Parsing Image Path ($i)\n"); + } + + # Get the full path of the destination + my $img_dst = "$::host_dir" . "$img"; + if ((-e "$img_dst") || (-l "$img_dst")) { + Print::print_err( +"An image by the same name already exists in the Host directory ($img)\n" + . "Use the browser's back button to fix the name or delete the existing file." + ); + } + + my $orig_size = (stat("$i"))[7]; + + # Copy, Move, or link it + if ($import_type == $IMG_ADD_SYM) { + + Print::log_host_info( +"Sym Linking image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/ln -s '$i' '$img_dst'"); + } + elsif ($import_type == $IMG_ADD_COPY) { + Print::log_host_info( +"Copying image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/cp '$i' '$img_dst'"); + } + elsif ($import_type == $IMG_ADD_MOVE) { + Print::log_host_info( +"Moving image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/mv '$i' '$img_dst'"); + } + + my $new_size = (stat("$img_dst"))[7]; + + if ($new_size != $orig_size) { + Print::print_err( +"Original image size ($orig_size) is not the same as the destination size ($new_size)" + ); + } + + # Add the disk and partition images to the config file + $imgid = Caseman::add_img_host_config("image", "$itype $img", $imgid); + } + print "Image file added with ID <tt>$imgid</tt><br>\n"; + + # AFM files have raw files that we also need to copy + # This approach is not the best, since it may copy more than + # is needed + if ($itype eq "afm") { + my $afm_base_path = ""; + + if ($img_path =~ /^(.*?)\.afm/i) { + $afm_base_path = $1; + $afm_base_path .= ".[0-9][0-9][0-9]"; + } + else { + Print::print_err( + "Error parsing out base name of AFM file $img_path"); + } + + print "BASE: $afm_base_path<br>\n"; + + my $copied = 0; + + foreach my $i (glob($afm_base_path)) { + $copied++; + + # remove the tainting + $i = $1 if ($i =~ /^($::REG_IMG_PATH)$/); + + # Deterine the local (target) name + my $img = ""; + if ($i =~ /\/($::REG_FILE)$/) { + $img = "$::IMGDIR/$1"; + } + else { + Print::print_err("Error Parsing Image Path ($i)\n"); + } + + # Get the full path of the destination + my $img_dst = "$::host_dir" . "$img"; + if ((-e "$img_dst") || (-l "$img_dst")) { + Print::print_err( +"An image by the same name already exists in the Host directory ($img) (AFM import)\n" + . "Use the browser's back button to fix the name or delete the existing file." + ); + } + + my $orig_size = (stat("$i"))[7]; + + # Copy, Move, or link it + if ($import_type == $IMG_ADD_SYM) { + + Print::log_host_info( +"Sym Linking image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/ln -s '$i' '$img_dst'"); + } + elsif ($import_type == $IMG_ADD_COPY) { + Print::log_host_info( +"Copying image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/cp '$i' '$img_dst'"); + } + elsif ($import_type == $IMG_ADD_MOVE) { + Print::log_host_info( +"Moving image $img_path into $Args::args{'case'}:$Args::args{'host'}" + ); + + Exec::exec_sys("/bin/mv '$i' '$img_dst'"); + } + + my $new_size = (stat("$img_dst"))[7]; + + if ($new_size != $orig_size) { + Print::print_err( +"Original image size ($orig_size) is not the same as the destination size ($new_size) after AFM import" + ); + } + } + if ($copied == 0) { + Print::print_err( +"No AFM raw files were found with the same base name and a numeric extension" + ); + } + else { + print "$copied AFM raw files imported<br>\n"; + } + } + + Caseman::update_md5("$imgid", "$act_md5") + unless (($do_md5 == $MD5_NOTHING) || ($imgid eq "")); + + # Add a disk entry if the image is of a disk + unless (($add_num == 1) && ($end[0] == 0) && ($start[0] == 0)) { + unless ((exists $Args::args{'vstype'}) + && ($Args::args{'vstype'} =~ /^(\w+)$/)) + { + Print::print_err("Missing Volume System Type"); + } + my $vstype = $Args::args{'vstype'}; + + my $diskid = Caseman::add_vol_host_config("disk", "$imgid $vstype"); + print "<p>Disk image (type $vstype) added with ID <tt>$diskid</tt>\n"; + } + + # Add the file system / partition entries + for (my $i = 0; $i < $add_num; $i++) { + my $volid = + Caseman::add_vol_host_config("part", + "$imgid $start[$i] $end[$i] $ftype[$i] $mnt[$i]"); + print +"<p>Volume image ($start[$i] to $end[$i] - $ftype[$i] - $mnt[$i]) added with ID <tt>$volid</tt>\n"; + } + + print <<EOF; +<p> +<center> +<table width=600> +<tr> + <td width=300 align=center> + <a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::VOL_OPEN&${Args::baseargs_novol}\"> + <img src=\"pict/menu_b_ok.jpg\" alt=\"Ok\" width=\"167\" height=20 border=\"0\"> + </a> + </td> + <td width=300 align=center> + <a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::IMG_ADD&${Args::baseargs_novol}\"> + <img src=\"pict/menu_b_inew.jpg\" alt=\"Ok\" width=\"167\" height=20 border=\"0\"> + </a> + </td> +</tr> +</table> +</center> + +EOF + Print::print_html_footer(); + + return 0; +} + +# Display details of image based on config values +# provides links to remove the config of the image and to get the file +# system details + +sub vol_details { + Print::print_html_header("Details of $Args::args{'vol'}"); + + Args::get_unitsize(); + + my $vol = Args::get_vol('vol'); + + my $mnt = $Caseman::vol2mnt{$vol}; + my $ftype = $Caseman::vol2ftype{$vol}; + + print "<center>" + . "<img src=\"pict/menu_h_idet.jpg\" alt=\"Image Details\">" + . "<br><br><br>\n" + . "<table width=\"600\" cellspacing=\"0\" cellpadding=\"2\" " + . "background=\"$::YEL_PIX\" border=0>\n" + . " <tr><td colspan=\"2\"> </td></tr>\n" + . + + # Name + " <tr><td align=\"right\" width=\"300\"><b>Name:</b></td>" + . "<td align=\"left\"><tt>$Caseman::vol2sname{$vol}</tt></td></tr>\n" + . "<tr><td align=\"right\" width=\"300\"><b>Volume Id:</b></td>" + . "<td align=\"left\"><tt>$vol</tt></td></tr>\n" + . "<tr><td align=\"right\" width=\"300\"><b>Parent Volume Id:</b></td>" + . "<td align=\"left\"><tt>$Caseman::vol2par{$vol}</tt></td></tr>\n" + . "<tr><td align=\"right\" width=\"300\"><b>Image File Format:</b></td>" + . "<td align=\"left\"><tt>$Caseman::vol2itype{$vol}</tt></td></tr>\n" + + # Mount + . " <tr><td align=\"right\"><b>Mounting Point:</b></td>" + . "<td align=\"left\"><tt>$mnt</tt></td></tr>\n" + . + + # Type + " <tr><td align=\"right\"><b>File System Type:</b></td>" + . "<td align=\"left\"><tt>$ftype</tt></td></tr>\n"; + + # Host Directory + print " <tr><td colspan=\"2\"> </td></tr>\n" + + # Strings File + . " <tr><td colspan=2 align=\"center\"><b>External Files</b></td></tr>\n" + . " <tr><td align=\"right\"><b>ASCII Strings:</b></td>" + . "<td align=\"left\"><tt>" + . ( + (exists $Caseman::vol2str{$vol}) + ? $Caseman::vol2sname{$Caseman::vol2str{$vol}} + : " " + ) + . "</tt></td></tr>\n" + . + + # Unicode Strings File + " <tr><td align=\"right\"><b>Unicode Strings:</b></td>" + . "<td align=\"left\"><tt>" + . ( + (exists $Caseman::vol2uni{$vol}) + ? $Caseman::vol2sname{$Caseman::vol2uni{$vol}} + : " " + ) + . "</tt></td></tr>\n"; + + if (($ftype ne "raw") && ($ftype ne "swap")) { + + # blkls file + print +" <tr><td align=\"right\"><b>Unallocated $Fs::addr_unit{$ftype}s:</b></td>" + . "<td align=\"left\"><tt>" + . ( + (exists $Caseman::vol2blkls{$vol}) + ? $Caseman::vol2sname{$Caseman::vol2blkls{$vol}} + : " " + ) + . "</tt></td></tr>\n"; + + # Strings of blkls + print + " <tr><td align=\"right\"><b>ASCII Strings of Unallocated:</b></td>" + . "<td align=\"left\"><tt>" + . ( + ( + (exists $Caseman::vol2blkls{$vol}) + && (exists $Caseman::vol2str{$Caseman::vol2blkls{$vol}}) + ) + ? $Caseman::vol2sname{$Caseman::vol2str{$Caseman::vol2blkls{$vol}}} + : " " + ) + . "</tt></td></tr>\n"; + + # Unicodde Strings of blkls + print +" <tr><td align=\"right\"><b>Unicode Strings of Unallocated:</b></td>" + . "<td align=\"left\"><tt>" + . ( + ( + (exists $Caseman::vol2blkls{$vol}) + && (exists $Caseman::vol2uni{$Caseman::vol2blkls{$vol}}) + ) + ? $Caseman::vol2sname{$Caseman::vol2uni{$Caseman::vol2blkls{$vol}}} + : " " + ) + . "</tt></td></tr>\n"; + } + + print " <tr><td colspan=\"2\"> </td></tr>\n" + . "</table>\n<a name=\"extract\"\n"; + + # Section for Strings file and 'blkls' file + + if ( + (!(exists $Caseman::vol2str{$vol})) + || (!(exists $Caseman::vol2uni{$vol})) + || (!(exists $Caseman::vol2blkls{$vol})) + || ( + (exists $Caseman::vol2blkls{$vol}) + && ( (!(exists $Caseman::vol2str{$Caseman::vol2blkls{$vol}})) + || (!(exists $Caseman::vol2uni{$Caseman::vol2blkls{$vol}}))) + ) + ) + { + print "<hr><table width=600>\n<tr>"; + } + + # Strings File + if ( (!(exists $Caseman::vol2str{$vol})) + || (!(exists $Caseman::vol2uni{$vol}))) + { + + print +"<td align=\"center\" width=280><h3>Extract Strings of<br>Entire Volume</h3>" + . "Extracting the ASCII and Unicode strings from a file system will " + . "make keyword searching faster.<br><br>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "Generate MD5? " + . "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED><br><br>" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_MAKESTR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Args::args{'vol'}\">\n" + . Args::make_hidden(); + + if (!(exists $Caseman::vol2str{$vol})) { + print +"ASCII: <input type=\"checkbox\" name=\"str\" value=\"1\" CHECKED> \n"; + } + if (!(exists $Caseman::vol2uni{$vol})) { + print +" Unicode: <input type=\"checkbox\" name=\"uni\" value=\"1\" CHECKED>\n"; + } + + print "<br><br><input type=\"image\" src=\"pict/srch_b_str.jpg\" " + . "alt=\"Extract Strings\" border=\"0\">\n</form></td>\n" + . "<td width=40> </td>\n"; + } + + if (($ftype eq 'blkls') || ($ftype eq 'swap') || ($ftype eq 'raw')) { + + # Place holder for types that have no notion of unallocated + } + + # Unallocated Space File + elsif (!(exists $Caseman::vol2blkls{$vol})) { + + print +"<td align=\"center\" width=280><h3>Extract Unallocated $Fs::addr_unit{$ftype}s</h3>" + . "Extracting the unallocated data in a file system allows " + . "more focused keyword searches and data recovery.<br><br>\n" + . "(Note: This Does Not Include Slack Space)<br>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n"; + + print "Generate MD5? " + . "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED><br><br>" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_MAKEBLKLS\">\n" + . + + "<input type=\"hidden\" name=\"vol\" value=\"$Args::args{'vol'}\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/srch_b_un.jpg\" " + . "alt=\"Extract Unallocated Data\" border=\"0\">\n<br></form>\n"; + } + + # strings of 'blkls' + elsif ((!(exists $Caseman::vol2str{$Caseman::vol2blkls{$vol}})) + || (!(exists $Caseman::vol2uni{$Caseman::vol2blkls{$vol}}))) + { + + print +"<td align=\"center\" width=280><h3>Extract Strings of<br>Unallocated $Fs::addr_unit{$ftype}s</h3>" + . "Extracting the ASCII strings from the unallocated data will make " + . "keyword searching faster.<br><br>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "Generate MD5? " + . "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED><br><br>" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_MAKESTR\">\n" + . + +"<input type=\"hidden\" name=\"vol\" value=\"$Caseman::vol2blkls{$vol}\">\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n" + . Args::make_hidden(); + + if (!(exists $Caseman::vol2str{$Caseman::vol2blkls{$vol}})) { + print +"ASCII: <input type=\"checkbox\" name=\"str\" value=\"1\" CHECKED> \n"; + } + if (!(exists $Caseman::vol2uni{$Caseman::vol2blkls{$vol}})) { + print +" Unicode: <input type=\"checkbox\" name=\"uni\" value=\"1\" CHECKED>\n"; + } + print "<br><br><input type=\"image\" src=\"pict/srch_b_str.jpg\" " + . "alt=\"Extract Strings\" border=\"0\">\n</form></td>\n"; + } + if ( + (!(exists $Caseman::vol2str{$vol})) + || (!(exists $Caseman::vol2uni{$vol})) + || (!(exists $Caseman::vol2blkls{$vol})) + || ( + (exists $Caseman::vol2blkls{$vol}) + && ( (!(exists $Caseman::vol2str{$Caseman::vol2blkls{$vol}})) + || (!(exists $Caseman::vol2uni{$Caseman::vol2blkls{$vol}}))) + ) + ) + { + print "</tr></table><hr>\n"; + } + + print "<p>" + . "<table width=\"400\" cellspacing=\"0\" cellpadding=\"2\">\n" + . + + # Ok + "<tr><td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_OPEN\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/menu_b_close.jpg\" " + . "alt=\"Close\" width=\"167\" height=20 border=0></form></td>\n"; + + print "<td align=center width=200>"; + if (($ftype ne "raw") && ($ftype ne "swap")) { + + # File System Details + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_blank\">\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_FS\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"image\" src=\"pict/menu_b_fs.jpg\" " + . "width=167 height=20 " + . "alt=\"File System\" border=0></form></td>\n"; + } + else { + print " </td>\n"; + } + +# Remove Image +# THis was removed 12/03 because it causes problems because the image still +# exists and config entries and ... it becomes a mess +# print +# "<td align=center width=200>". +# "<form action=\"$::PROGNAME\" method=\"get\">\n". +# "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n". +# "<input type=\"hidden\" name=\"view\" value=\"$Caseman::IMG_DEL\">\n". +# Args::make_hidden(). +# "<input type=\"hidden\" name=\"vol\" value=\"$Args::args{'vol'}\">\n". +# "<input type=\"hidden\" name=\"mnt\" value=\"$Args::args{'mnt'}\">\n". +# "<input type=\"image\" src=\"pict/menu_b_rem.jpg\" ". +# "width=167 height=20 alt=\"Remove\" border=0></form>". +# "</td>\n". +# "</tr></table>\n"; + + Print::print_html_footer(); + return 0; +} + +# remove the config files +sub img_del { + Args::check_vol('vol'); + + # Args::check_ftype(); + Print::print_html_header( + "Removing Configuration Settings for $Args::args{'vol'}"); + + Caseman::del_host_config("", $Args::args{'vol'}, ""); + Caseman::update_md5($Args::args{'vol'}, ""); + + print "Settings for <tt>$Args::args{'vol'}</tt> removed from " + . "<tt>$Args::args{'case'}:$Args::args{'host'}</tt>.\n" + . "<p>NOTE: The actual file still exists in the host directory.\n"; + + print "<p><a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_OPEN&${Args::baseargs_novol}\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\"></a>\n"; + + Print::print_html_footer(); + + return 0; +} + +# Make a strings -t d file for the image to decrease the search time +# Can make both ASCII and Unicode strings files +sub vol_makestr { + Print::print_html_header("Extracting Strings"); + + my $ascii = 0; + my $uni = 0; + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + if ((exists $Args::args{'str'}) && ($Args::args{'str'} == 1)) { + if (exists $Caseman::vol2str{$vol}) { + Print::print_err( +"Image already has an ASCII strings file: $Caseman::vol2sname{$vol}" + ); + } + $ascii = 1; + } + + if ((exists $Args::args{'uni'}) && ($Args::args{'uni'} == 1)) { + if (exists $Caseman::vol2uni{$vol}) { + Print::print_err( +"Image already has a Unicode strings file: $Caseman::vol2sname{$vol}" + ); + } + + $uni = 1; + } + if (($uni == 0) && ($ascii == 0)) { + goto str_egress; + } + + my $base_name = $Caseman::vol2sname{$vol}; + + if ($ascii == 1) { + my $fname_rel = "$::DATADIR/${base_name}-$ftype.asc"; + my $fname = "$::host_dir" . "$fname_rel"; + + if (-e "$fname") { + my $i = 1; + $i++ while (-e "$::host_dir" + . "$::DATADIR/" + . "${base_name}-$ftype-$i.asc"); + + $fname_rel = "$::DATADIR/${base_name}-$ftype-$i.asc"; + $fname = "$::host_dir" . "$fname_rel"; + } + + print +"Extracting ASCII strings from <tt>$Caseman::vol2sname{$vol}</tt><br>\n"; + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Saving ASCII strings to $fname_rel"); + + local *OUT; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + + if ($ftype eq "blkls") { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/srch_strings' -a -t d $img > '$fname'"); + } + elsif ((($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d > '$fname'"); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d > '$fname'" + ); + } + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + + print $_ while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + + print "<br>\n" if ($hit_cnt != 0); + + # Verify that it worked + unless (open(STR, "$fname")) { + print( "Error opening $fname<br>\n" + . "Either an error occurred while generating the file or " + . "no ASCII strings exist<br>"); + goto str_uni; + } + + # append to config + my $strvol = + Caseman::add_vol_host_config("strings", "$vol $fname_rel"); + print "Host configuration file updated<br>"; + + $Caseman::vol2ftype{$strvol} = "strings"; + $Caseman::mod2vol{$strvol} = $vol; + $Caseman::vol2str{$vol} = $strvol; + $Caseman::vol2cat{$strvol} = "mod"; + $Caseman::vol2itype{$strvol} = "raw"; + + $Caseman::vol2par{$strvol} = $vol; + $Caseman::vol2path{$strvol} = "$::host_dir" . "$fname_rel"; + $Caseman::vol2start{$strvol} = 0; + $Caseman::vol2end{$strvol} = 0; + $Caseman::vol2sname{$strvol} = $fname_rel; + + # Calculate MD5 + if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) { + print "Calculating MD5 Value<br><br>\n"; + my $m = Hash::int_create_wrap($strvol); + print "MD5 Value: <tt>$m</tt><br><br>\n"; + } + } + + str_uni: + + if ($uni == 1) { + + my $fname_rel = "$::DATADIR/${base_name}-$ftype.uni"; + my $fname = "$::host_dir" . "$fname_rel"; + + if (-e "$fname") { + my $i = 1; + $i++ while (-e "$::host_dir" + . "$::DATADIR/" + . "${base_name}-$ftype-$i.uni"); + + $fname_rel = "$::DATADIR/${base_name}-$ftype-$i.uni"; + $fname = "$::host_dir" . "$fname_rel"; + } + + print "<hr>\n" if ($ascii == 1); + + print +"Extracting Unicode strings from <tt>$Caseman::vol2sname{$vol}</tt><br>\n"; + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Saving Unicode strings to $fname_rel"); + + local *OUT; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + if ($ftype eq "blkls") { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/srch_strings' -a -t d -e l $img > '$fname'"); + } + elsif ((($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d -e l > '$fname'"); + } + + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -e l > '$fname'" + ); + } + + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + + print $_ while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + + print "<br>\n" if ($hit_cnt != 0); + + # Verify that it worked + unless (open(STR, "$fname")) { + print "Error opening $fname<br>\n" + . "Either an error occurred while generating the file or " + . "no Unicode strings exist"; + goto str_egress; + } + + # append to config + my $strvol = + Caseman::add_vol_host_config("unistrings", "$vol $fname_rel"); + print "Host configuration file updated<br>"; + + $Caseman::vol2ftype{$strvol} = "strings"; + $Caseman::mod2vol{$strvol} = $vol; + $Caseman::vol2uni{$vol} = $strvol; + $Caseman::vol2cat{$strvol} = "mod"; + $Caseman::vol2itype{$strvol} = "raw"; + + $Caseman::vol2par{$strvol} = $vol; + $Caseman::vol2path{$strvol} = "$::host_dir" . "$fname_rel"; + $Caseman::vol2start{$strvol} = 0; + $Caseman::vol2end{$strvol} = 0; + $Caseman::vol2sname{$strvol} = $fname_rel; + + # Calculate MD5 + if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) { + print "Calculating MD5 Value<br><br>\n"; + $m = Hash::int_create_wrap($strvol); + print "MD5 Value: <tt>$m</tt><br><br>\n"; + } + } + + str_egress: + + my $dest_vol = $vol; + + # We need to return with a real image to VOL_DETAILS so check the mod + $dest_vol = $Caseman::mod2vol{$vol} + if (exists $Caseman::mod2vol{$vol}); + + print "<hr><a href=\"$::PROGNAME?$Args::baseargs_novol&mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_DETAILS&vol=$dest_vol\" target=_top>Image Details</a><p>\n"; + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_KWSRCH&$Args::baseargs\"" + . " target=\"_top\">Keyword Search</a>\n"; + + Print::print_html_footer(); + + return 0; +} + +sub vol_makeblkls { + Print::print_html_header("Extracting Unallocated Space"); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $base_name = $Caseman::vol2sname{$vol}; + $base_name = $1 if ($base_name =~ /^(.*?)\.dd$/); + my $fname_rel = "$::DATADIR/${base_name}-$ftype.unalloc"; + my $fname = "$::host_dir" . "$fname_rel"; + + if (-e "$::host_dir" . "$fname_rel") { + my $i = 1; + $i++ while (-e "$::host_dir" + . "$::DATADIR/" + . "${base_name}-$ftype-$i.unalloc"); + + $fname_rel = "$::DATADIR/${base_name}-$ftype-$i.unalloc"; + $fname = "$::host_dir" . "$fname_rel"; + } + + Print::log_host_inv( + "$Args::args{'vol'}: Saving unallocated data to $fname_rel"); + + print +"Extracting unallocated data from <tt>$Caseman::vol2sname{$vol}</tt><br>\n"; + + local *OUT; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -f $ftype -o $offset -i $imgtype $img > '$fname'"); + + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + + print "$_" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + + print "<br>\n" + if ($hit_cnt != 0); + + # append to config + my $blklsvol = Caseman::add_vol_host_config("blkls", "$vol $fname_rel"); + print "Host configuration file updated<br>"; + + $Caseman::vol2ftype{$blklsvol} = "blkls"; + $Caseman::mod2vol{$blklsvol} = $vol; + $Caseman::vol2blkls{$vol} = $blklsrvol; + $Caseman::vol2cat{$blklsvol} = "mod"; + $Caseman::vol2itype{$blklsvol} = "raw"; + + $Caseman::vol2par{$blklsvol} = $vol; + $Caseman::vol2path{$blklsvol} = "$::host_dir" . "$fname_rel"; + $Caseman::vol2start{$blklsvol} = 0; + $Caseman::vol2end{$blklsvol} = 0; + $Caseman::vol2sname{$blklsvol} = $fname_rel; + + # Calculate MD5 + if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) { + print "Calculating MD5 Value<br>\n"; + my $m = Hash::int_create_wrap($blklsvol); + print "MD5 Value: <tt>$m</tt><br><br>\n"; + } + + print "<a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_DETAILS\" target=_top>Image Details</a><p>\n"; + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_KWSRCH&$Args::baseargs_novol&" + . "vol=$fname_rel\" target=\"_top\">Keyword Search</a>\n"; + + Print::print_html_footer(); + return 0; +} + +1; diff --git a/lib/Data.pm b/lib/Data.pm new file mode 100644 index 0000000000000000000000000000000000000000..f8e2ef43fe671acd6661ed2634f6591dd56a8de3 --- /dev/null +++ b/lib/Data.pm @@ -0,0 +1,927 @@ +# +# Data / Content layer functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Data; + +$Data::FRAME = 1; +$Data::ENTER = 2; +$Data::CONT = 3; +$Data::CONT_MENU = 4; +$Data::CONT_MENU_FR = 5; +$Data::REPORT = 6; +$Data::LIST = 7; +$Data::EXPORT = 8; +$Data::BLANK = 9; + +# Display types that use the sort variable +$Data::SORT_ASC = 0; +$Data::SORT_HEX = 1; +$Data::SORT_STR = 2; + +# Types of block numbers +$Data::ADDR_DD = 0; +$Data::ADDR_BLKLS = 1; + +sub main { + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Data::FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the data unit address + if ($view == $Data::FRAME) { + return frame(); + } + elsif ($view == $Data::ENTER) { + return enter(); + } + elsif ($view == $Data::LIST) { + return list(); + } + elsif ($view == $Data::BLANK) { + return blank(); + } + + # These windows do need the data unit address + Args::check_block(); + if ($view == $Data::CONT) { + return content(); + } + elsif ($view == $Data::CONT_MENU) { + return content_menu(); + } + elsif ($view == $Data::CONT_MENU_FR) { + return content_menu_frame(); + } + elsif ($view == $Data::REPORT) { + return report(); + } + + elsif ($view == $Data::EXPORT) { + return export(); + } + else { + Print::print_check_err("Invalid Data View"); + } + +} + +# Generate the 2 frames for block browsing +sub frame { + Print::print_html_header_frameset("Data Browse on $Args::args{'vol'}"); + + print "<frameset cols=\"20%,80%\">\n"; + + # Data Contents + if (exists $Args::args{'block'}) { + my $len = Args::get_len(); + + print "<frame src=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::ENTER&" + . "$Args::baseargs&block=$Args::enc_args{'block'}\">\n" + . "<frame src=\"$::PROGNAME?" + . "mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "block=$Args::enc_args{'block'}&$Args::baseargs&len=$len\" " + . "name=\"content\">\n</frameset>\n"; + } + else { + print "<frame src=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::ENTER&" + . "$Args::baseargs\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::BLANK&" + . "$Args::baseargs\" name=\"content\">\n</frameset>\n"; + } + + Print::print_html_footer_frameset(); + return 0; +} + +# Frame to enter the data into +sub enter { + Print::print_html_header(""); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $bs = Args::get_unitsize(); + + print "<form action=\"$::PROGNAME\" method=\"get\" " + . "target=\"content\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_DATA\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Data::CONT_MENU_FR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . Args::make_hidden() + . + + # Address + "<b>$Fs::addr_unit{$ftype} Number:</b><br> " + . "<input type=\"text\" name=\"block\" size=12 maxlength=12"; + print " value=\"$Args::enc_args{'block'}\"" + if (exists $Args::args{'block'}); + print ">\n"; + + # Number of units + print "<p><b>Number of $Fs::addr_unit{$ftype}" . "s:</b>" + . "<br> " + . "<input type=\"text\" name=\"len\" value=\"1\" size=6 maxlength=6>\n"; + + print "<p><b>$Fs::addr_unit{$ftype} Size:</b>" . " $bs\n"; + + # blkls images do not get to select this + # if (($ftype ne 'blkls') && ($ftype ne 'swap') && ($ftype ne 'raw')) { + if ($Fs::is_fs{$ftype} == 1) { + print "<p><b>Address Type:</b><br> " + . "<select name=\"btype\" size=1>\n" + . "<option value=\"$Data::ADDR_DD\" selected>Regular (dd)</option>\n" + . "<option value=\"$Data::ADDR_BLKLS\">Unallocated (blkls)</option>\n" + . "</select>\n"; + } + else { + print + "<input type=\"hidden\" name=\"btype\" value=\"$Data::ADDR_DD\">\n"; + } + + # Lazarus + print "<p><b>Lazarus Addr:</b> " + . "<input type=\"checkbox\" name=\"btype2\">\n" + . "<p><input type=\"image\" src=\"pict/but_view.jpg\" " + . "width=45 height=22 alt=\"View\" border=\"0\">\n" + . "</form>\n"; + + print "<hr><p>" + . "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::LIST&$Args::baseargs\" target=\"content\">" + . "<img src=\"pict/but_alloc_list.jpg\" border=\"0\" " + . "width=113 height=20 alt=\"Allocation List\"></a><br>\n" + if ($Fs::is_fs{$ftype} == 1); + + # unless (($ftype eq 'blkls') || ($ftype eq 'swap') || ($ftype eq 'raw')); + + # If there is a blkls image, then provide a button for it + if (($ftype ne 'blkls') && (exists $Caseman::vol2blkls{$vol})) { + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_DATA\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::vol2blkls{$vol}\">\n" + . Args::make_hidden() + . "<p><input type=\"image\" src=\"pict/srch_b_lun.jpg\" " + . "alt=\"Load Unallocated Image\" border=\"0\">\n<br></form>\n"; + } + + # If we are using a blkls image, then give a button for the original + elsif (($ftype eq 'blkls') && (exists $Caseman::mod2vol{$vol})) { + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_DATA\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::mod2vol{$vol}\">\n" + . Args::make_hidden() + . "<p><input type=\"image\" src=\"pict/srch_b_lorig.jpg\" " + . "alt=\"Load Original Image\" border=\"0\">\n<br></form>\n"; + } + + Print::print_html_footer(); + return 0; +} + +# generate frame for block content +sub content_menu_frame { + Print::print_html_header_frameset(""); + + my $sort = $Data::SORT_ASC; + $sort = $1 + if ((exists $Args::args{'sort'}) && ($Args::args{'sort'} =~ /^(\d+)$/)); + + my $len = Args::get_len(); + if ($len == 0) { + Print::print_err("Invalid length: 0"); + } + + my $blk; + + my $ifind = Args::get_ifind(); + + # off is 1 if a lazarus block number as they are off by one + my $off = 0; + $off = -1 if (exists $Args::args{'btype2'}); + + # Do we need to convert from blkls value to dd value ? + if ( (exists $Args::args{'btype'}) + && ($Args::args{'btype'} == $Data::ADDR_BLKLS)) + { + + my $vol = Args::get_vol('vol'); + my $b = Args::get_block() + $off; + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkcalc' -f $ftype -u $b -o $offset -i $imgtype $img"); + $blk = Exec::read_pipe_line(*OUT); + close(OUT); + + $blk = "Error getting block" + if ((!defined $blk) || ($blk eq "")); + + if ($blk !~ /^\d+$/) { + print "$blk\n"; + return 1; + } + } + else { + $blk = Args::get_block() + $off; + } + + print "<frameset rows=\"25%,75%\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU&$Args::baseargs" + . "&block=$blk&sort=$sort&len=$len&ifind=$ifind\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT&$Args::baseargs" + . "&block=$blk&sort=$sort&len=$len\" name=\"cont2\">\n" + . "</frameset>"; + + Print::print_html_footer_frameset(); + return 0; +} + +sub print_ifind { + my $block = Args::get_block(); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Finding $Fs::meta_str{$ftype} for data unit $block" + ); + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ifind' -f $ftype -d $block -o $offset -i $imgtype $img"); + my $meta = Exec::read_pipe_line(*OUT); + close(OUT); + + $meta = "Error getting meta address" + if ((!defined $meta) || ($meta eq "")); + + if ($meta =~ /^($::REG_META)$/o) { + $meta = $1; + my $tmpr = $Caseman::vol2mnt{$vol}; + print "<b>Pointed to by $Fs::meta_str{$ftype}:</b> " + . "<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs&" + . "meta=$meta\" target=\"_top\">$meta</a><br>\n"; + + print "<b>Pointed to by file:</b>\n"; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ffind' -f $ftype -a -o $offset -i $imgtype $img $meta"); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + + # Make it red if it is deleted + if (/^(\*)\s+\/*(.*)$/) { + Print::print_output("<tt><font color=\"$::DEL_COLOR[0]\">" + . Print::html_encode(${tmpr} . ${2}) + . "</font></tt> (deleted)<br>\n"); + } + + # If it starts with a '/' then it must be a file + elsif (/^\/(.*)$/) { + Print::print_output("<tt>" + . Print::html_encode(${tmpr} . ${1}) + . "</tt><br>\n"); + } + + # this must be an error + else { + Print::print_output(Print::html_encode($_) . "<br>\n"); + } + } + close(OUT); + } + else { + print "$meta\n"; + } +} + +# Generate index for block content +sub content_menu { + Print::print_html_header(""); + + my $block = Args::get_block(); + my $prev = $block - 1; + my $next = $block + 1; + + my $sort = Args::get_sort(); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $ifind = Args::get_ifind(); + + my $len = Args::get_len(); + my $bs = Args::get_unitsize(); + + if ($len == 0) { + Print::print_err("Invalid length: 0"); + } + + print "<center>"; + my $url = + "$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "$Args::baseargs&sort=$sort&len=$len" + . "&ifind=$ifind"; + + # Next and Previous pointers + print "<table cellspacing=\"0\" cellpadding=\"2\">\n" . "<tr>\n"; + + # Previous + if ($prev < $Fs::first_addr{$ftype}) { + print "<td align=\"right\"> </td>\n"; + } + else { + print "<td align=\"right\">" + . "<a href=\"$url&block=$prev\" target=\"_parent\">\n" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a></td>\n"; + } + + # Next + print "<td align=\"left\"><a href=\"$url&block=$next\"" + . " target=\"_parent\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a></td>\n</tr>\n"; + + print "<tr><td align=\"right\"><a href=\"$::PROGNAME?" + . "mod=$::MOD_DATA&view=$Data::EXPORT&$Args::baseargs&" + . "block=$block&len=$len\">" + . "<img src=\"pict/but_export.jpg\" border=\"0\" alt=\"Export\" " + . "width=123 height=20></a></td>\n"; + + if ($::USE_NOTES == 1) { + print "<td align=\"left\">" + . "<a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::ENTER_DATA&$Args::baseargs&block=$block&len=$len\" " + . "target=\"_blank\">" + . "<img src=\"pict/but_addnote.jpg\" border=\"0\" " + . "width=\"89\" height=20 alt=\"Add Note\"></a></td>\n"; + } + else { + print "<td align=\"left\"> </td>\n"; + } + + print "</tr></table>\n"; + + # Display formats + print "<table cellspacing=\"0\" cellpadding=\"2\">\n" . "<tr><td>ASCII ("; + if ($sort == $Data::SORT_ASC) { + print "display - "; + } + else { + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "$Args::baseargs&" + . "sort=$Data::SORT_ASC&block=$block&len=$len\" target=\"_parent\">" + . "display</a> - \n"; + } + + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::REPORT&" + . "$Args::baseargs&sort=$Data::SORT_ASC" + . "&block=$block&len=$len\" target=\"_blank\">report</a>)</td>\n" + . "<td>*</td>\n"; + + print "<td>Hex ("; + if ($sort == $Data::SORT_HEX) { + print "display - "; + } + else { + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "$Args::baseargs&" + . "sort=$Data::SORT_HEX&block=$block&len=$len\" target=\"_parent\">" + . "display</a> - \n"; + } + + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::REPORT&" + . "$Args::baseargs&sort=$Data::SORT_HEX" + . "&block=$block&len=$len\" target=\"_blank\">report</a>)</td>\n" + . "<td>*</td>\n"; + + print "<td>ASCII Strings ("; + if ($sort == $Data::SORT_STR) { + print "display - "; + } + else { + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "$Args::baseargs&" + . "sort=$Data::SORT_STR&block=$block&len=$len\" target=\"_parent\">" + . "display</a> - \n"; + } + + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::REPORT&" + . "$Args::baseargs&sort=$Data::SORT_STR" + . "&block=$block&len=$len\" target=\"_blank\">report</a>)</td>\n" + . "</tr></table>\n"; + + # Special case for 'blkls' b.c. we need to specify original data unit size + local *OUT; + if ($ftype eq 'blkls') { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype -u $bs -o $offset -i $imgtype $img $block | '$::FILE_EXE' -z -b -" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype -o $offset -i $imgtype $img $block | '$::FILE_EXE' -z -b -" + ); + } + my $file_type = Exec::read_pipe_line(*OUT); + close(OUT); + + $file_type = "Error getting file type" + if ((!defined $file_type) || ($file_type eq "")); + + print "<b>File Type:</b> $file_type<br></center>\n"; + + if ($len == 1) { + print "<b>$Fs::addr_unit{$ftype}:</b> $block<br>\n"; + } + else { + my $end = $block + $len - 1; + print "<b>$Fs::addr_unit{$ftype}" . "s:</b> $block-$end<br>\n"; + } + if ($Fs::is_fs{$ftype} == 1) { + + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkstat' -f $ftype -o $offset -i $imgtype $img $block" + ); + + my $cnt = 0; + while ($_ = Exec::read_pipe_line(*OUT)) { + + if ($_ =~ /((Not )?Allocated)/) { + print "<font color=\"$::DEL_COLOR[0]\">" if (defined $2); + print "<b>Status:</b> $1<br>"; + print "</font>" if (defined $2); + } + elsif ($_ =~ /Group: (\d+)/) { + print "<b>Group:</b> $1<br>\n"; + } + $cnt++; + } + close(OUT); + if ($cnt == 0) { + print "Invalid $Fs::addr_unit{$ftype} address<br>\n"; + return; + } + + # Make ifind an option + $url = + "$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU&" + . "$Args::baseargs&sort=$sort&len=$len&block=$block"; + if ($ifind == 0) { + print "<a href=\"$url&ifind=1\">Find Meta Data Address</a><br>\n"; + } + else { + print "<a href=\"$url&ifind=0\">Hide Meta Data Address</a><br>\n"; + print_ifind(); + } + } + + # Option to view original if it exists + if ( ($ftype eq 'blkls') + && (exists $Caseman::mod2vol{$vol})) + { + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&" + . "view=$Data::CONT_MENU_FR&${Args::baseargs_novol}" + . "&vol=$Caseman::mod2vol{$vol}&" + . "block=$block&sort=$sort&len=$len&btype=$Data::ADDR_BLKLS\" " + . "target=\"_parent\">View Original</a><br>\n"; + } + + Print::print_html_footer(); + return 0; +} + +#Display actual block content +sub content { + Args::check_sort(); + + Print::print_text_header(); + + my $sort = Args::get_sort(); + my $block = Args::get_block(); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $len = Args::get_len(); + my $bs = Args::get_unitsize(); + + my $range = ""; + if ($len == 0) { + print "Invalid length: 0\n"; + exit(1); + } + elsif ($len == 1) { + $range = "$Fs::addr_unit{$ftype} $block"; + } + else { + my $end = $block + $len - 1; + $range = "$Fs::addr_unit{$ftype}" . "s $block-$end"; + } + my $str = "Contents of $range in $Caseman::vol2sname{$vol}\n\n\n"; + my $log_str = "contents of $range"; + + my $usize_str = ""; + $usize_str = " -u $bs " + if ($ftype eq 'blkls'); + + local *OUT; + if ($sort == $Data::SORT_HEX) { + print "Hex " . $str; + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Displaying Hex $log_str"); + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -h -o $offset -i $imgtype $img $block $len" + ); + } + elsif ($sort == $Data::SORT_ASC) { + print "ASCII " . $str; + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Displaying ASCII $log_str"); + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -a -o $offset -i $imgtype $img $block $len" + ); + } + elsif ($sort == $Data::SORT_STR) { + print "ASCII String " . $str; + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Displaying string $log_str"); + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -o $offset -i $imgtype $img $block $len | '$::TSKDIR/srch_strings' -a" + ); + } + print $_ while ($_ = Exec::read_pipe_data(*OUT, 512)); + close(OUT); + + Print::print_text_footer(); + + return 0; +} + +sub report { + Args::check_sort(); + + my $sort = Args::get_sort(); + my $vol = Args::get_vol('vol'); + my $block = Args::get_block(); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $len = Args::get_len(); + my $type; + + if ($len == 0) { + print("Invalid length: 0"); + exit(1); + } + my $bs = Args::get_unitsize(); + + my $usize_str = ""; + $usize_str = " -u $bs " if ($ftype eq 'blkls'); + + Print::print_text_header("$vol" . "-" + . "$Fs::addr_unit{$ftype}" + . "$Args::args{'block'}" + . ".txt"); + + if ($sort == $Data::SORT_ASC) { + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Generating ASCII report on data unit $block" + ); + $type = "ascii"; + } + elsif ($sort == $Data::SORT_STR) { + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Generating ASCII strings report on data unit $block" + ); + $type = "string"; + } + elsif ($sort == $Data::SORT_HEX) { + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Generating hex report on data unit $block" + ); + $type = "hex"; + } + else { + print "\n\n"; + print "invalid sort value"; + return 1; + } + + print " Autopsy $type $Fs::addr_unit{$ftype} Report\n\n" + . "-" x 70 . "\n" + . " GENERAL INFORMATION\n\n"; + + if ($len == 1) { + print "$Fs::addr_unit{$ftype}: $Args::args{'block'}\n"; + } + else { + my $end = $block + $len - 1; + print "$Fs::addr_unit{$ftype}" . "s: $Args::args{'block'}-$end\n"; + } + print "$Fs::addr_unit{$ftype} Size: $bs\n"; + + # if (($ftype ne 'blkls') && ($ftype ne 'raw') && ($ftype ne 'swap')) { + if ($Fs::is_fs{$ftype} == 1) { + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ifind' -f $ftype -d $block -o $offset -i $imgtype $img" + ); + my $meta = Exec::read_pipe_line(*OUT); + close(OUT); + + $meta = "Error getting meta address" + if ((!defined $meta) || ($meta eq "")); + + if ($meta =~ /^($::REG_META)$/o) { + my $tmpi = $1; + print "\nPointed to by $Fs::meta_str{$ftype}: $tmpi\n"; + + my $tmpr = $Caseman::vol2mnt{$vol}; + print "Pointed to by files:\n"; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ffind' -f $ftype -a -o $offset -i $imgtype $img $tmpi" + ); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + if (/^(\*)\s+\/*(.*)$/) { + Print::print_output( + " $tmpr$2 (deleted)\n"); + } + elsif (/^\/(.*)$/) { + Print::print_output( + " $tmpr$1\n"); + } + else { + Print::print_output(" $_\n"); + } + } + close(OUT); + } + else { + print "Not allocated to any meta data structures\n"; + } + } # not blkls + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -o $offset -i $imgtype $img $block $len | '$::MD5_EXE'" + ); + my $md5 = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5 = "Error getting md5" + if ((!defined $md5) || ($md5 eq "")); + + chop $md5; + print "MD5 of raw $Fs::addr_unit{$ftype}: $md5\n"; + + if ($sort == $Data::SORT_HEX) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -h -o $offset -i $imgtype $img $block $len | '$::MD5_EXE'" + ); + } + elsif ($sort == $Data::SORT_ASC) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -a -o $offset -i $imgtype $img $block $len | '$::MD5_EXE'" + ); + } + elsif ($sort == $Data::SORT_STR) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -o $offset -i $imgtype $img $block $len | '$::TSKDIR/srch_strings' -a | '$::MD5_EXE'" + ); + } + + $md5 = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5 = "Error getting md5" + if ((!defined $md5) || ($md5 eq "")); + + chop $md5; + print "MD5 of $type output: $md5\n"; + + print "\nImage: $Caseman::vol2path{$vol}\n"; + if (($Caseman::vol2start{$vol} == 0) && ($Caseman::vol2end{$vol} == 0)) { + print "Offset: Full image\n"; + } + elsif ($Caseman::vol2end{$vol} == 0) { + print "Offset: $Caseman::vol2start{$vol} to end\n"; + } + else { + print "Offset: $Caseman::vol2start{$vol} to $Caseman::vol2end{$vol}\n"; + } + print "File System Type: $ftype\n"; + + my $date = localtime(); + + print "\nDate Generated: $date\n" + . "Investigator: $Args::args{'inv'}\n" + . "-" x 70 . "\n" + . " CONTENT\n\n"; + + if ($sort == $Data::SORT_HEX) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -h -o $offset -i $imgtype $img $block $len" + ); + } + elsif ($sort == $Data::SORT_ASC) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -a -o $offset -i $imgtype $img $block $len" + ); + } + elsif ($sort == $Data::SORT_STR) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype $usize_str -o $offset -i $imgtype $img $block $len | '$::TSKDIR/srch_strings' -a" + ); + } + Print::print_output($_) + while ($_ = Exec::read_pipe_data(*OUT, 512)); + close(OUT); + + print "\n" + . "-" x 70 . "\n" + . " VERSION INFORMATION\n\n" + . "Autopsy Version: $::VER\n"; + print "The Sleuth Kit Version: " . ::get_tskver() . "\n"; + + Print::print_text_footer(); + + return 0; +} + +# +# Display the block allocation list +# +sub list { + Print::print_html_header("Block Allocation List"); + + my $BLKLS_GAP = 500; + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $min = 0; + $min = Args::get_min() if (exists $Args::args{'min'}); + my $max = $min + $BLKLS_GAP - 1; + + # set fmin to the minimum for the file system + my $fmin = $min; + $fmin = $Fs::first_addr{$ftype} if ($min < $Fs::first_addr{$ftype}); + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Block Allocation List for $min to $max"); + print "<center><H2>$Fs::addr_unit{$ftype}: $min - $max</H2>"; + + my $tmp; + + if ($min - $BLKLS_GAP >= 0) { + $tmp = $min - $BLKLS_GAP; + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::LIST&" + . "$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a> "; + } + $tmp = $min + $BLKLS_GAP; + print " <a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::LIST&" + . "$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a><br>"; + print "</center>\n"; + + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -el -f $ftype -o $offset -i $imgtype $img $fmin-$max" + ); + while ($_ = Exec::read_pipe_line(*OUT)) { + if (/^(\d+)\|([af])/) { + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&" + . "view=$Data::CONT_MENU_FR&$Args::baseargs&block=$1\">" + . "$1:</a> "; + if ($2 eq "a") { + print "allocated<br>\n"; + } + else { + print "<font color=\"$::DEL_COLOR[0]\">free</font><br>\n"; + } + } + } + close(OUT); + + print "<center>\n"; + if ($min - $BLKLS_GAP >= 0) { + $tmp = $min - $BLKLS_GAP; + print "<a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::LIST&" + . "$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a> "; + } + $tmp = $min + $BLKLS_GAP; + print " <a href=\"$::PROGNAME?mod=$::MOD_DATA&view=$Data::LIST&" + . "$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a><br>"; + print "</center>\n"; + + Print::print_html_footer(); + return 0; +} + +sub export { + my $block = Args::get_block(); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $len = Args::get_len(); + my $bs = Args::get_unitsize(); + + Print::print_oct_header( + "$vol" . "-" . "$Fs::addr_unit{$ftype}" . "$block" . ".raw"); + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Saving contents of data unit $block (unit size: $bs number: $len)" + ); + + local *OUT; + if ($ftype eq 'blkls') { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype -u $bs -o $offset -i $imgtype $img $block $len" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkcat' -f $ftype -o $offset -i $imgtype $img $block $len" + ); + } + print "$_" while ($_ = Exec::read_pipe_data(*OUT, 512)); + close(OUT); + + Print::print_oct_footer(); + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header("Data Unit Blank Page"); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + + print "<center><h3>Data Unit Mode</h3><br>\n" + . "Here you can view the contents of any $Fs::addr_unit{$ftype} in the file system.<br>\n" + . "Enter the address in the field on the left.\n"; + Print::print_html_footer(); + return 0; +} diff --git a/lib/Exec.pm b/lib/Exec.pm new file mode 100644 index 0000000000000000000000000000000000000000..e13871275673d3e1b2b7fbbfc958f7956c646362 --- /dev/null +++ b/lib/Exec.pm @@ -0,0 +1,91 @@ +# +# Functions that wrap the execution of tools so that they are logged +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Exec; + +# exec_pipe(HANDLE, CMD); +sub exec_pipe { + my $handle = shift(@_); + my $cmd = shift(@_); + + die "Can't open pipe for exec_pipe" + unless defined(my $pid = open($handle, '-|')); + + if ($pid) { + return $handle; + } + else { + $| = 1; + Print::log_host_inv_exec("$cmd"); + exec("$cmd") or die "Can't exec program: $!"; + } +} + +sub read_pipe_line { + my $handle = shift(@_); + my $out; + +# for (my $i = 0; ($len = read ($handle, $$buf, $size)) && (!defined $len); $i++) { + + for (my $i = 0; $i < 100; $i++) { + $out = <$handle>; + return $out if (defined $out); + } + return $out; +} + +sub read_pipe_data { + my $handle = shift(@_); + my $size = shift(@_); + my $out; + my $len; + + for ( + my $i = 0; + ($len = read($handle, $out, $size)) && (!defined $len) && ($i < 100); + $i++ + ) + { + } + + return $out; +} + +sub exec_sys { + my $cmd = shift(@_); + Print::log_host_inv_exec("$cmd"); + system($cmd); + return; +} + +1; diff --git a/lib/File.pm b/lib/File.pm new file mode 100644 index 0000000000000000000000000000000000000000..59696d013cb2ba05195a368e1cf9b9af68ecbd5d --- /dev/null +++ b/lib/File.pm @@ -0,0 +1,2310 @@ +# +# File name layer functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Updated 1/15 + +package File; + +# If the order of these views are changed, then the checks in main +# must be as well +$File::BLANK = 0; +$File::FRAME = 1; +$File::DIR_LIST = 2; +$File::FILE_LIST_DIR = 3; +$File::FILE_LIST_FILE = 4; +$File::FILE_LIST_DEL = 5; +$File::FILE_LIST = 6; +$File::CONT = 7; +$File::CONT_FR = 8; +$File::CONT_MENU = 9; +$File::REPORT = 10; +$File::EXPORT = 11; +$File::MD5LIST = 12; +$File::CONT_IMG = 13; + +$File::REC_NO = 0; +$File::REC_YES = 1; + +sub main { + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $File::FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view == $File::BLANK) { + blank(); + return 0; + } + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the meta data address + if ($view < $File::FILE_LIST) { + + if ($view == $File::FRAME) { + return frame(); + } + + Args::check_dir(); + if ($view == $File::DIR_LIST) { + return dir_list(); + } + elsif ($view == $File::FILE_LIST_DIR) { + return file_list_dir(); + } + elsif ($view == $File::FILE_LIST_DEL) { + return file_list_del(); + } + elsif ($view == $File::FILE_LIST_FILE) { + return file_list_file(); + } + } + + # These windows need the meta data address + Args::check_dir(); + Args::check_meta('meta'); + + if ($view < $File::REPORT) { + if ($view == $File::FILE_LIST) { + return file_list(); + } + elsif ($view == $File::CONT) { + return content(); + } + elsif ($view == $File::CONT_FR) { + return content_fr(); + } + elsif ($view == $File::CONT_MENU) { + return content_menu(); + } + } + else { + if ($view == $File::REPORT) { + return report(); + } + elsif ($view == $File::EXPORT) { + return export(); + } + elsif ($view == $File::MD5LIST) { + return md5list(); + } + elsif ($view == $File::CONT_IMG) { + return content_img(); + } + } + + Print::print_check_err("Invalid File View"); +} + +# Sorting and display types +my $FIL_SORT_ASC = 0; +my $FIL_SORT_STR = 1; +my $FIL_SORT_HEX = 2; + +# Methods of sorting the file listings +my $SORT_DTYPE = 0; # type according to dentry +my $SORT_ITYPE = 1; # type according to meta +my $SORT_NAME = 2; +my $SORT_MOD = 3; +my $SORT_ACC = 4; +my $SORT_CHG = 5; +my $SORT_CRT = 6; +my $SORT_SIZE = 7; +my $SORT_GID = 8; +my $SORT_UID = 9; +my $SORT_META = 10; +my $SORT_DEL = 11; + +my $DIRMODE_SHOW = 1; +my $DIRMODE_NOSHOW = 2; + +# +# Make the three frames and fill them in +# +sub frame { + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + + # If we were not given the meta, then look up the root + unless (exists $Args::args{'meta'}) { + $Args::args{'meta'} = $Args::enc_args{'meta'} = $Fs::root_meta{$ftype}; + } + + unless (exists $Args::args{'dir'}) { + $Args::enc_args{'dir'} = $Args::args{'dir'} = "/"; + } + + Args::check_meta('meta'); + Args::check_dir(); + + my $meta = Args::get_meta('meta'); + my $dir = Args::get_dir(); + + Print::print_html_header_frameset("$mnt$dir on $Args::args{'vol'}"); + + my $sort = $SORT_NAME; + $sort = $Args::args{'sort'} if (exists $Args::args{'sort'}); + + my $dirmode = $DIRMODE_NOSHOW; + $dirmode = $Args::args{'dirmode'} if (exists $Args::args{'dirmode'}); + + print "<frameset cols=\"175,*\">\n"; + + # Directory Listing on Left + my $url = + "$::PROGNAME?$Args::baseargs&dir=$dir&" . "sort=$sort&dirmode=$dirmode"; + + print "<frame src=\"$url&mod=$::MOD_FILE&view=$File::DIR_LIST\">\n"; + + # File frameset on right + print "<frameset rows=\"50%,50%\">\n"; + + # File Listings on top + print + "<frame src=\"$url&mod=$::MOD_FILE&view=$File::FILE_LIST&meta=$meta\" " + . "name=\"list\">\n"; + + # File Contents + print "<frame src=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::BLANK&" + . "$Args::baseargs\" name=\"content\">\n" + . "</frameset>\n" + . "</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# +# Print the directory names for the lhs frame and other +# Search forms +# + +sub dir_list { + Args::check_sort(); + Args::check_dirmode(); + + Print::print_html_header(""); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $sort = Args::get_sort(); + my $dirmode = Args::get_dirmode(); + my $mnt = $Caseman::vol2mnt{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $lcldir = ""; + my $prev_plus = ""; # previous number of '+' directory spacers + my $prev_fname = ""; + my $prev_meta = ""; + + # Field to enter a directory into: + print "<p><form action=\"$::PROGNAME\" method=\"get\" target=\"list\">\n" + . "<center><b>Directory Seek</b></center><br>" + . "Enter the name of a directory that you want to view.<br>" + . "<tt>$mnt</tt>" + . "<input type=\"text\" name=\"dir\" size=24 maxlength=100>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FILE\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$File::FILE_LIST_DIR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"sort\" value=\"$Args::args{'sort'}\">\n" + . "<input type=\"hidden\" name=\"dirmode\" value=\"$Args::args{'dirmode'}\">\n" + . Args::make_hidden() + . + + # View Button + "<br><input type=\"image\" src=\"pict/but_view.jpg\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></form>\n"; + + # Field to enter a name into: + print + "<hr><p><form action=\"$::PROGNAME\" method=\"get\" target=\"list\">\n" + . "<center><b>File Name Search</b></center><br>" + . "Enter a Perl regular expression for the file names you want to find.<br><br>\n" + . "<input type=\"text\" name=\"dir\" size=24 maxlength=100>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FILE\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$File::FILE_LIST_FILE\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Args::args{'vol'}\">\n" + . "<input type=\"hidden\" name=\"sort\" value=\"$Args::args{'sort'}\">\n" + . "<input type=\"hidden\" name=\"dirmode\" value=\"$Args::args{'dirmode'}\">\n" + . Args::make_hidden() + . "<br>\n" + . + + # Search Button + "<input type=\"image\" src=\"pict/but_search.jpg\" " + . "width=61 height=22 alt=\"Search\" border=\"0\"></form>\n"; + + print "<p><hr><p>\n"; + + my $base_url = "$::PROGNAME?$Args::baseargs&sort=$sort"; + + # All deleted files button + print "<a href=\"$base_url&mod=$::MOD_FILE&view=$File::FILE_LIST_DEL&" + . "dir=&dirmode=$dirmode\" target=\"list\">" + . "<img border=\"0\" src=\"pict/file_b_alldel.jpg\" width=\"127\" " + . "alt=\"Show All Deleted Files\">" + . "</a><p>\n"; + + # The dirmode arg shows if we should expand the whole directory listing + # or not + if ($dirmode == $DIRMODE_NOSHOW) { + print "<a href=\"$base_url&mod=$::MOD_FILE&view=$File::FRAME&" + . "dirmode=$DIRMODE_SHOW\" target=\"_parent\">" + . "<img src=\"pict/file_b_expand.jpg\" alt=\"Expand All Directories\" " + . "border=\"0\"></a><p><hr>\n"; + + return; + } + else { + print "<a href=\"$base_url&mod=$::MOD_FILE&view=$File::FRAME&" + . "dirmode=$DIRMODE_NOSHOW\" target=\"_parent\">" + . "<img src=\"pict/file_b_hide.jpg\" alt=\"Hide All Directories\" " + . "border=\"0\"></a><p><hr>\n"; + } + + $base_url .= "&dirmode=$dirmode"; + + Print::log_host_inv("$Args::args{'vol'}: List of all directories"); + + # We need to maintain state to create dir and this is done by + # counting the + marks. + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/fls' -f $ftype -ruD -o $offset -i $imgtype $img"); + + # Print root + my $url = + "$base_url&mod=$::MOD_FILE&view=$File::FILE_LIST&" + . "meta=$Fs::root_meta{$ftype}&dir="; + print "<p><a href=\"$url\" target=\"list\">$mnt</a><br>\n"; + + while ($_ = Exec::read_pipe_line(*OUT)) { + if ( +/^(\*)?(\+*)\s*[\-d]\/[\-d]\s*(\d+)\-?\d*\-?\d*\s*(\(realloc\))?:\t(.+)$/ + ) + { + + my $del = $1; + my $plus = $2; + my $meta = $3; + my $re = $4; + my $fname = $5; + + # Adjust the dir value using the '++' values to determine + # how "deep" we are + unless ($prev_plus eq $plus) { + + # are we in 1 more + if ($plus eq $prev_plus . '+') { + $lcldir .= ($prev_fname . "/"); + } + + # we are back (at least one) + elsif (defined $plus) { + my @dirs = split('/', $lcldir); + my $idx = -1; + $lcldir = ""; + + while (($idx = index($plus, '+', $idx + 1)) != -1) { + $lcldir .= ($dirs[$idx] . "/"); + } + } + } + + $prev_plus = $plus; + $prev_fname = $fname; + $prev_meta = $meta; + + $url = + "$base_url&mod=$::MOD_FILE&view=$File::FILE_LIST&" + . "meta=$meta&dir=" + . Args::url_encode($lcldir . $fname . "/"); + + print "<font color=\"$::DEL_COLOR[0]\">" if defined $del; + print "+$plus<a href=\"$url\" target=\"list\"><tt>/" + . Print::html_encode($fname) + . "</tt></a><br>\n"; + print "</font>" if defined $del; + } + } + close(OUT); + Print::print_html_footer(); + return 0; + +}; # end of FIL_DIR + +# Print the files and directories for the upper rhs frame +# These can be sorted in any format +# +# We need to find a way to cache this data +# +sub file_list { + Args::check_sort(); + Args::check_dirmode(); + + my $vol = Args::get_vol('vol'); + my $sort = Args::get_sort(); + my $ftype = $Caseman::vol2ftype{$vol}; + my $meta = Args::get_meta('meta'); + my $mnt = $Caseman::vol2mnt{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + + my $sp = " "; + + Print::print_html_header("Entries in $fname"); + + my (@itype, @dtype, @name, @mod, @acc, @chg, @crt, @size, @gid, @uid, + @meta); + my (@dir, @entry, @del, @realloc, @meta_int); + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Directory listing of $fname ($meta)"); + + local *OUT; + + # execute command + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' -f $ftype -la $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + + # Make the big table, small table, and start the current directory + + my $iurl = +"$::PROGNAME?$Args::baseargs&dirmode=$Args::enc_args{'dirmode'}&sort=$sort"; + + # base number of columns in table + my $cols = 15; + $cols += 2 if ($Fs::has_ctime{$ftype}); + $cols += 2 if ($Fs::has_crtime{$ftype}); + $cols += 2 if ($Fs::has_mtime{$ftype}); + + print <<EOF1; +<!-- Big Table --> +<table cellspacing=\"0\" cellpadding=\"2\" border=0> + +<!-- Small Table --> +<tr> + <td colspan=$cols> + <table border=0 align=\"left\" cellspacing=\"0\" cellpadding=\"2\" width=500> + <tr> + <td colspan=2><b>Current Directory:</b> <tt> + + <a href=\"${iurl}&mod=$::MOD_FILE&view=$File::FILE_LIST&meta=$Fs::root_meta{$ftype}&dir=\">$mnt</a> + +EOF1 + + # Each file in the path will get its own link + $iurl .= "&mod=$::MOD_FILE&view=$File::FILE_LIST_DIR"; + my $path = ""; + my @dir_split = split('/', $Args::args{'dir'}); + while (scalar @dir_split > 1) { + my $d = shift @dir_split; + + next if ($d eq ''); + + $path .= "$d/"; + print " <a href=\"${iurl}&dir=$path\">/${d}/</a> \n"; + } + print " /$dir_split[0]/ \n" + if (scalar @dir_split == 1); + + print " </tt></td>\n" . " </tr>\n"; + + # Add Note Button + $iurl = +"&$Args::baseargs&dir=$Args::enc_args{'dir'}&meta=$Args::enc_args{'meta'}"; + if ($::USE_NOTES == 1) { + + print <<EOF2; + <tr> + <td width=\"100\" align=left> + <a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::ENTER_FILE$iurl\" target=\"_blank\"> + <img border=\"0\" src=\"pict/but_addnote.jpg\" width=\"89\" height=20 alt=\"Add Note About Directory\"> + </a> + </td> +EOF2 + + } + + # Generate MD5 List Button + print <<EOF3; + + <td width=\"206\" align=left> + <a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::MD5LIST$iurl\" target=\"_blank\"> + <img border=\"0\" src=\"pict/file_b_md5list.jpg\" width=\"206\" alt=\"Generate list of MD5 values\"> + </a> + </td> + </tr> + <!-- END of Little Table --> + </table> + </td> +</tr> +<tr> + <td colspan=$cols><hr></td> +</tr> + +EOF3 + + # Make the Table and Headers + my $url = + "$::PROGNAME?mod=$::MOD_FILE&view=$File::FRAME&" + . "$Args::baseargs&meta=$Args::enc_args{'meta'}" + . "&dir=$Args::enc_args{'dir'}&dirmode=$Args::enc_args{'dirmode'}"; + + print "<tr valign=\"MIDDLE\" " . "background=\"$::YEL_PIX\">\n"; + + # Print the Headers - If the sorting mode is set to it, then don't + # make it a link and print a different button + if ($sort == $SORT_DEL) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_del_cur.jpg\" " + . "width=\"49\" height=20 " + . "alt=\"Deleted Files\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_DEL"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_del_link.jpg\" " + . "width=\"28\" height=20 " + . "alt=\"Deleted Files\">" + . "</a></td>\n"; + } + + # type only gets one column for two 'types' + print " <td background=\"$::YEL_PIX\">$sp</td>\n" + . " <th align=\"center\" background=\"$::YEL_PIX\">" + . " Type <br>"; + + if ($sort == $SORT_DTYPE) { + print "dir"; + } + else { + $iurl = $url . "&sort=$SORT_DTYPE"; + print "<a href=\"$iurl\" target=\"_parent\">dir</a>"; + } + + print " / "; + + if ($sort == $SORT_ITYPE) { + print "in</th>\n"; + } + else { + $iurl = $url . "&sort=$SORT_ITYPE"; + print "<a href=\"$iurl\" target=\"_parent\">in</a></th>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + if ($sort == $SORT_NAME) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_nam_cur.jpg\" " + . "width=\"76\" height=20 " + . "alt=\"File Name\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_NAME"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_nam_link.jpg\" " + . "width=\"50\" height=20 " + . "alt=\"File Name\">" + . "</a></td>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + # Modified / Written + if ($Fs::has_mtime{$ftype}) { + if ($sort == $SORT_MOD) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_wr_cur.jpg\" " + . "width=\"89\" height=20 " + . "alt=\"Modified/Written Time\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_MOD"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_wr_link.jpg\" " + . "width=\"60\" height=20 " + . "alt=\"Modified/Written Time\">" + . "</a></td>\n"; + } + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + } + + # Accessed + if ($sort == $SORT_ACC) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_acc_cur.jpg\" " + . "width=\"90\" height=20 " + . "alt=\"Access Time\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_ACC"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_acc_link.jpg\" " + . "width=\"66\" height=20 " + . "alt=\"Access Time\">" + . "</a></td>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + # Change + if ($Fs::has_ctime{$ftype}) { + if ($sort == $SORT_CHG) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_chg_cur.jpg\" " + . "width=\"90\" height=20 " + . "alt=\"Change Time\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_CHG"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_chg_link.jpg\" " + . "width=\"62\" height=20 " + . "alt=\"Change Time\">" + . "</a></td>\n"; + } + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + } + + # Create + if ($Fs::has_crtime{$ftype}) { + if ($sort == $SORT_CRT) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_cre_cur.jpg\" " + . "width=\"84\" height=20 " + . "alt=\"Create Time\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_CRT"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_cre_link.jpg\" " + . "width=\"59\" height=20 " + . "alt=\"Create Time\">" + . "</a></td>\n"; + } + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + } + + # Size + if ($sort == $SORT_SIZE) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_siz_cur.jpg\" " + . "width=\"53\" height=20 " + . "alt=\"Size\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_SIZE"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_siz_link.jpg\" " + . "width=\"31\" height=20 " + . "alt=\"Size\">" + . "</a></td>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + # UID + if ($sort == $SORT_UID) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_uid_cur.jpg\" " + . "width=\"49\" height=20 " + . "alt=\"UID\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_UID"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_uid_link.jpg\" " + . "width=\"27\" height=20 " + . "alt=\"UID\">" + . "</a></td>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + # GID + if ($sort == $SORT_GID) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_gid_cur.jpg\" " + . "width=\"49\" height=20 " + . "alt=\"GID\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_GID"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_gid_link.jpg\" " + . "width=\"28\" height=20 " + . "alt=\"GID\">" + . "</a></td>\n"; + } + + print " <td background=\"$::YEL_PIX\">$sp</td>\n"; + + # meta + if ($sort == $SORT_META) { + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_meta_cur.jpg\" " + . "width=\"62\" height=20 " + . "alt=\"Meta\">" + . "</td>\n"; + } + else { + $iurl = $url . "&sort=$SORT_META"; + print " <td align=\"left\" background=\"$::YEL_PIX\">" + . "<a href=\"$iurl\" target=\"_parent\">" + . "<img border=\"0\" " + . "src=\"pict/file_h_meta_link.jpg\" " + . "width=\"41\" height=20 " + . "alt=\"Meta\">" + . "</a></td>\n"; + } + print "</tr>\n"; + + my $cnt = 0; + my %seen; + + # sort fls into arrays + while ($_ = Exec::read_pipe_line(*OUT)) { + if ( +/^($::REG_MTYPE)\/($::REG_MTYPE)\s*(\*?)\s*($::REG_META)(\(realloc\))?:\t(.+?)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t(\d+)\t(\d+)\t(\d+)$/o + ) + { + + my $lcldir = $Args::args{'dir'}; + $dtype[$cnt] = $1; + $itype[$cnt] = $2; + $del[$cnt] = $3; + $meta[$cnt] = $4; + $realloc[$cnt] = ""; + $realloc[$cnt] = $5 if (defined $5); + $name[$cnt] = $6; + $mod[$cnt] = $7; + $acc[$cnt] = $8; + $chg[$cnt] = $9; + $crt[$cnt] = $10; + $size[$cnt] = $11; + $gid[$cnt] = $12; + $uid[$cnt] = $13; + + if ($meta[$cnt] =~ /^(\d+)(-\d+(-\d+)?)?$/) { + $meta_int[$cnt] = $1; + } + else { + $meta_int[$cnt] = $meta[$cnt]; + } + + # See if we have already seen this file yet + if (exists $seen{"$name[$cnt]-$meta[$cnt]"}) { + my $prev_cnt = $seen{"$name[$cnt]-$meta[$cnt]"}; + + # If we saw it while it was deleted, & it + # is now undel, then update it + if ( ($del[$cnt] eq "") + && ($del[$prev_cnt] eq '*')) + { + $del[$prev_cnt] = $del[$cnt]; + } + next; + + # Add it to the seen list + } + else { + $seen{"$name[$cnt]-$meta[$cnt]"} = $cnt; + } + + # We must adjust the dir for directories + if ($itype[$cnt] eq 'd') { + + # special cases for .. and . + if ($name[$cnt] eq '..') { + my @dirs = split('/', $lcldir); + my $i; + $lcldir = ""; + for ($i = 0; $i < $#dirs; $i++) { + $lcldir .= ($dirs[$i] . '/'); + } + } + elsif ($name[$cnt] ne '.') { + $lcldir .= ($name[$cnt] . '/'); + } + $name[$cnt] .= '/'; + } + else { + $lcldir .= $name[$cnt]; + } + + # format the date so that the time and time zone are on the + # same line + $mod[$cnt] = "$1 $2" + if ($mod[$cnt] =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + $acc[$cnt] = "$1 $2" + if ($acc[$cnt] =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + $chg[$cnt] = "$1 $2" + if ($chg[$cnt] =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + $crt[$cnt] = "$1 $2" + if ($crt[$cnt] =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + $dir[$cnt] = Args::url_encode($lcldir); + $entry[$cnt] = $cnt; + $cnt++; + + } + + # We missed it for some reason + else { + print +"<tr><td colspan=10>Error Parsing File (Invalid Characters?):<br>$_</td></tr>\n"; + } + } + close(OUT); + + if ($cnt == 0) { + print "</table>\n<center>No Contents</center>\n"; + return 0; + } + + # Sort the above array based on the sort argument + my @sorted; # an array of indices + + if ($sort == $SORT_DTYPE) { + @sorted = + sort { $dtype[$a] cmp $dtype[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_ITYPE) { + @sorted = + sort { $itype[$a] cmp $itype[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_NAME) { + @sorted = sort { lc($name[$a]) cmp lc($name[$b]) } @entry; + } + elsif ($sort == $SORT_MOD) { + @sorted = + sort { $mod[$a] cmp $mod[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_ACC) { + @sorted = + sort { $acc[$a] cmp $acc[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_CHG) { + @sorted = + sort { $chg[$a] cmp $chg[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_CRT) { + @sorted = + sort { $crt[$a] cmp $crt[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_SIZE) { + @sorted = + sort { $size[$a] <=> $size[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_UID) { + @sorted = + sort { $uid[$a] <=> $uid[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_GID) { + @sorted = + sort { $gid[$a] <=> $gid[$b] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + elsif ($sort == $SORT_META) { + @sorted = sort { + $meta_int[$a] <=> $meta_int[$b] + or lc($name[$a]) cmp lc($name[$b]) + } @entry; + } + elsif ($sort == $SORT_DEL) { + @sorted = + sort { $del[$b] cmp $del[$a] or lc($name[$a]) cmp lc($name[$b]) } + @entry; + } + + # print them based on sorting + my $row = 0; + foreach my $i (@sorted) { + my $url; + my $target; + my $color; + my $lcolor; + if ($del[$i] eq '*') { + $color = + "<font color=\"" . $::DEL_COLOR[$realloc[$i] ne ""] . "\">"; + $lcolor = $color; + } + else { + $color = "<font color=\"$::NORM_COLOR\">"; + $lcolor = "<font color=\"$::LINK_COLOR\">"; + } + + # directories have different targets and view values + if ($itype[$i] eq 'd') { + $target = "list"; + $url = + "$::PROGNAME?mod=$::MOD_FILE&view=$File::FILE_LIST&" + . "$Args::baseargs&meta=$meta_int[$i]" + . "&sort=$sort&dir=$dir[$i]&dirmode=$Args::enc_args{'dirmode'}"; + } + else { + $target = "content"; + $url = + "$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR&" + . "$Args::baseargs&meta=$meta[$i]" + . "&sort=$sort&dir=$dir[$i]&dirmode=$Args::enc_args{'dirmode'}"; + if ($del[$i] eq '*') { + $url .= "&recmode=$File::REC_YES"; + } + else { + $url .= "&recmode=$File::REC_NO"; + } + } + + if (($row % 2) == 0) { + print +"<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR\">\n <td align=\"center\">"; + } + else { + print +"<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR_TABLE\">\n <td align=\"center\">"; + } + + print "<img src=\"pict/file_b_check.jpg\" border=\"0\">\n" + if ($del[$i] eq '*'); + + print "</td>\n" + . " <td>$sp</td>\n" + . " <td align=\"center\">${color}$dtype[$i] / $itype[$i]</td>\n" + . " <td>$sp</td>\n"; + + # for valid files and directories make a link + # Special rule for $OrphanFiles directory and HFS directories, which have a size of 0 + if ( + ($meta_int[$i] >= $Fs::first_meta{$ftype}) + && (($size[$i] > 0) || (($name[$i] =~ /^\$Orphan/) && ($itype[$i] eq 'd')) || (($ftype =~ /hfs/) && ($itype[$i] eq 'd'))) + && ( ($itype[$i] eq 'r') + || ($itype[$i] eq 'd') + || ($itype[$i] eq 'v')) + ) + { + print " <td><a href=\"$url\" target=\"$target\">$lcolor"; + } + else { + print " <td>$color"; + } + print "<tt>" + . Print::html_encode($name[$i]) + . "</tt></td>\n" + . " <td>$sp</td>\n"; + print " <td>${color}$mod[$i]</td>\n" . " <td>$sp</td>\n" + if ($Fs::has_mtime{$ftype}); + print " <td>${color}$acc[$i]</td>\n" . " <td>$sp</td>\n"; + print " <td>${color}$chg[$i]</td>\n" . " <td>$sp</td>\n" + if ($Fs::has_ctime{$ftype}); + print " <td>${color}$crt[$i]</td>\n" . " <td>$sp</td>\n" + if ($Fs::has_crtime{$ftype}); + print " <td>${color}$size[$i]</td>\n" + . " <td>$sp</td>\n" + . " <td>${color}$uid[$i]</td>\n" + . " <td>$sp</td>\n" + . " <td>${color}$gid[$i]</td>\n" + . " <td>$sp</td>\n"; + + # for a valid meta, make a link to meta browsing mode + if ($meta_int[$i] >= $Fs::first_meta{$ftype}) { + my $iurl = +"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs&meta=$meta[$i]"; + print "<td><a href=\"$iurl\" target=\"_top\">$lcolor"; + } + else { + print "<td>$color"; + } + print "$meta[$i]</a> $realloc[$i]</td>\n</tr>\n"; + + $row++; + } + + print "</table>\n"; + Print::print_html_footer(); + return 0; + +}; #end of FIL_LIST + +# This takes a directory name as an argument and converts it to +# the meta value and calls FIL_LIST +# +# The meta value can be anything when this is run, it will be +# overwritten +sub file_list_dir { + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $dir = Args::get_dir(); + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + Print::log_host_inv( + "$Args::args{'vol'}: Finding meta data address for $dir"); + + # Use 'ifind -n' to get the meta data address for the given name + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ifind' -f $ftype -n '$dir' -o $offset -i $imgtype $img"); + my $meta; + + while ($_ = Exec::read_pipe_line(*OUT)) { + $meta = $1 + if (/^($::REG_META)$/); + } + close(OUT); + + Print::print_check_err("Error finding meta data address for $dir") + unless (defined $meta); + + Print::print_check_err("Error finding meta data address for $dir: $meta") + unless ($meta =~ /^$::REG_META$/); + + # Verify it is a directory with istat + Exec::exec_pipe(*OUT, + "'$::TSKDIR/istat' -f $ftype -o $offset -i $imgtype $img $meta"); + + while ($_ = Exec::read_pipe_line(*OUT)) { + + # This is a directory + if ( (/mode:\s+d/) + || (/File Attributes: Directory/) + || (/^Flags:.*?Directory/)) + { + close(OUT); + + # Set the meta variables + $Args::enc_args{'meta'} = $Args::args{'meta'} = $meta; + + $Args::args{'dir'} .= "/" + unless ($Args::args{'dir'} =~ /.*?\/$/); + $Args::enc_args{'dir'} .= "/" + unless ($Args::enc_args{'dir'} =~ /.*?\/$/); + + # List the directory contents + file_list(); + + return 0; + } + } + close(OUT); + + # This is not a directory, so just give a link + Print::print_html_header(""); + + my $meta_int = $meta; + $meta_int = $1 if ($meta_int =~ /(\d+)-\d+(-\d+)?/); + + my $recmode = $File::REC_NO; + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ils' -f $ftype -e -o $offset -i $imgtype $img $meta_int"); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + next unless ($_ =~ /^$meta/); + if ($_ =~ /^$meta\|f/) { + $recmode = $File::REC_YES; + } + elsif ($_ =~ /^$meta\|a/) { + $recmode = $File::REC_NO; + } + else { + Print::print_err("Error parsing ils output: $_"); + } + } + close(OUT); + + print <<EOF; + +<tt>$dir</tt> ( +<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs&meta=$meta&recmode=$recmode\" target=\"_top\"> +meta $meta</a>) is not a directory. + +<p> +<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR&$Args::baseargs&meta=$meta&dir=$dir&recmode=$recmode\" target=\"content\"> + <img src=\"pict/but_viewcont.jpg\" height=20 width=123 alt=\"view contents\" border=\"0\"> +</a> + +EOF + + Print::print_html_footer(); + return 1; + +} + +# List the files that meet a certain pattern +sub file_list_file { + Args::check_sort(); + Args::check_dirmode(); + Args::check_dir(); + + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $ftype = $Caseman::vol2ftype{$vol}; + my $meta = $Fs::root_meta{$ftype}; + my $sort = Args::get_sort(); + my $dirmode = Args::get_dirmode(); + my $dir = Args::get_dir(); + + Print::print_html_header( + "Filtered files on $Caseman::vol2sname{$vol} $mnt"); + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + my $sp = " "; + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Listing all files with $dir"); + + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' -f $ftype -lpr $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + + print "<b>All files with \'<tt>$dir</tt>\' in the name</b><p>\n" + . "<a href=\"$::PROGNAME?$Args::baseargs&dirmode=$Args::enc_args{'dirmode'}" + . "&sort=$sort&mod=$::MOD_FILE&view=$File::FILE_LIST" + . "&meta=$Fs::root_meta{$ftype}&dir=\">" + . "<img border=\"0\" src=\"pict/file_b_allfiles.jpg\" width=\"112\" " + . "alt=\"Show All Files\"></a>\n" . "<hr>" + . "<table cellspacing=\"0\" cellpadding=\"2\" border=0>\n" + . "<tr valign=\"MIDDLE\" align=\"left\" " + . "background=\"$::YEL_PIX\">\n"; + + # deleted + print "<td align=\"left\">" + . "<img border=\"0\" src=\"pict/file_h_del_link.jpg\" " + . "width=\"28\" height=20 alt=\"Deleted Files\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Type + print "<th align=\"center\"> Type  <br>" + . "dir / in</th>" + . "<td>$sp</td>\n"; + + # Name + print " <td><img border=\"0\" " + . "src=\"pict/file_h_nam_link.jpg\" " + . "width=\"50\" height=20 " + . "alt=\"File Name\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Mod / Written + if ($Fs::has_mtime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_wr_link.jpg\" " + . "width=\"60\" " + . "alt=\"Written Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Access + print " <td><img border=\"0\" " + . "src=\"pict/file_h_acc_link.jpg\" " + . "width=\"66\" height=20 " + . "alt=\"Access Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Change + if ($Fs::has_ctime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_chg_link.jpg\" " + . "width=\"62\" " + . "alt=\"Change Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Create + if ($Fs::has_crtime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_cre_link.jpg\" " + . "width=\"59\" " + . "alt=\"Create Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Size + print " <td><img border=\"0\" " + . "src=\"pict/file_h_siz_link.jpg\" " + . "width=\"31\" height=20 " + . "alt=\"Size\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # UID + print " <td><img border=\"0\" " + . "src=\"pict/file_h_uid_link.jpg\" " + . "width=\"27\" height=20 " + . "alt=\"UID\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # GID + print " <td><img border=\"0\" " + . "src=\"pict/file_h_gid_link.jpg\" " + . "width=\"28\" height=20 " + . "alt=\"GID\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Meta + print " <td><img border=\"0\" " + . "src=\"pict/file_h_meta_link.jpg\" " + . "width=\"41\" height=20 " + . "alt=\"Meta\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + my $row = 0; + while ($_ = Exec::read_pipe_line(*OUT)) { + if ( +/^($::REG_MTYPE)\/($::REG_MTYPE)\s*(\*?)\s*($::REG_META)(\(realloc\))?:\t(.+?)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t(\d+)\t(\d+)\t(\d+)$/o + ) + { + + # We have to remove the / from the beginning of the file name so + # save all values so they aren't lost + my $dt = $1; + my $it = $2; + my $d = $3; + my $i = $4; + my $r = 0; + $r = 1 if (defined $5); + my $n = $6; + my $m = $7; + my $a = $8; + my $c = $9; + my $cr = $10; + my $s = $11; + my $g = $12; + my $u = $13; + + if ($n =~ /^\/(.*)/) { + $n = $1; + } + + my $p = ""; + my $f = $n; + + if ($n =~ /^(.+?)\/([^\/]+)$/) { + $p = $1; + $f = $2; + } + + next unless ($f =~ /$dir/i); + + my $enc_n = Args::url_encode($n); + my $iurl = +"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs&meta=$i"; + my $i_int = $i; + $i_int = $1 if ($i =~ /(\d+)-\d+-\d+/); + + if (($row % 2) == 0) { + print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR\">\n"; + } + else { + print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR_TABLE\">\n"; + } + + print "<td align=\"left\">\n"; + + my $color; + my $lcolor; + if ($d eq '*') { + $color = "<font color=\"" . $::DEL_COLOR[$r] . "\">"; + $lcolor = $color; + + print "<img src=\"pict/file_b_check.jpg\" border=\"0\">\n"; + + } + else { + $color = "<font color=\"$::NORM_COLOR\">"; + $lcolor = "<font color=\"$::LINK_COLOR\">"; + print " "; + } + + print "</td><td>$sp</td>"; + + print "<td align=\"center\">$color" + . "$dt / $it</td>" + . "<td>$sp</td>\n"; + + if ($it eq 'd') { + my $url = + "$::PROGNAME?mod=$::MOD_FILE&" + . "view=$File::FILE_LIST&$Args::baseargs&meta=$i" + . "&sort=$sort&dir=$enc_n&dirmode=$dirmode"; + + print "<td>"; + if ($i_int >= $Fs::first_meta{$ftype}) { + print "<a href=\"$url\" target=\"_self\">$lcolor"; + } + else { + print "$color"; + } + print "<tt>" + . Print::html_encode($mnt . $n) + . "</tt></td>" + . "<td>$sp</td>\n"; + } + else { + my $url = + "$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR&" + . "$Args::baseargs&meta=$i&sort=$sort&dir=$enc_n"; + + if ($d eq '*') { + $url .= "&recmode=$File::REC_YES"; + } + else { + $url .= "&recmode=$File::REC_NO"; + } + + print "<td>"; + if (($i_int >= $Fs::first_meta{$ftype}) && ($it eq 'r')) { + print "<a href=\"$url\" target=\"content\">$lcolor"; + } + else { + print "$color"; + } + print "<tt>$mnt$n</tt></td>" . "<td>$sp</td>\n"; + } + + $m = "$1 $2" + if ($m =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $a = "$1 $2" + if ($a =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $c = "$1 $2" + if ($c =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $cr = "$1 $2" + if ($cr =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + print "<td>$color$m</td>" . "<td>$sp</td>\n" + if ($Fs::has_mtime{$ftype}); + + print "<td>$color$a</td>" . "<td>$sp</td>\n"; + print "<td>$color$c</td>" . "<td>$sp</td>\n" + if ($Fs::has_ctime{$ftype}); + print "<td>$color$cr</td>" . "<td>$sp</td>\n" + if ($Fs::has_crtime{$ftype}); + + print "<td>$color$s</td>" + . "<td>$sp</td>\n" + . "<td>$color$g</td>" + . "<td>$sp</td>\n" + . "<td>$color$u</td>" + . "<td>$sp</td>\n"; + + print "<td>"; + if ($i_int >= $Fs::first_meta{$ftype}) { + print "<a href=\"$iurl\" target=\"_top\">"; + print "$lcolor$i</a>"; + } + else { + print "$color$i"; + } + print " (realloc)" if $r; + print "</td></tr>\n"; + } + else { + print "Error Parsing File (invalid characters?)<br>: $_\n<br>"; + } + + $row++; + } + close(OUT); + print "</table>\n"; + + print "<center>No files found with that pattern</center>\n" + if ($row == 0); + + Print::print_html_footer(); + return 0; +} + +# display deleted files only +# +# Sorting should be added to this +sub file_list_del { + Args::check_sort(); + Args::check_dirmode(); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $mnt = $Caseman::vol2mnt{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $meta = $Fs::root_meta{$ftype}; + my $sort = Args::get_sort(); + my $dirmode = Args::get_dirmode(); + + Print::print_html_header("Deleted files on $Caseman::vol2sname{$vol} $mnt"); + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + my $sp = " "; + + Print::log_host_inv("$Caseman::vol2sname{$vol}: Listing all deleted files"); + + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' -f $ftype -ldr $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + + print "<b>All Deleted Files</b><p><hr>" + . "<table cellspacing=\"0\" cellpadding=\"2\" border=0>\n" + . "<tr valign=\"MIDDLE\" align=\"left\" " + . "background=\"$::YEL_PIX\">\n"; + + # Type + print "<th align=\"center\"> Type  <br>" + . "dir / in</th>" + . "<td>$sp</td>\n"; + + # Name + print " <td><img border=\"0\" " + . "src=\"pict/file_h_nam_link.jpg\" " + . "width=\"50\" height=20 " + . "alt=\"File Name\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Mod / Written + if ($Fs::has_mtime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_wr_link.jpg\" " + . "width=\"60\" " + . "alt=\"Written Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Access + print " <td><img border=\"0\" " + . "src=\"pict/file_h_acc_link.jpg\" " + . "width=\"66\" height=20 " + . "alt=\"Access Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Change + if ($Fs::has_ctime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_chg_link.jpg\" " + . "width=\"62\" " + . "alt=\"Change Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Create + if ($Fs::has_crtime{$ftype}) { + print " <td><img border=\"0\" " + . "src=\"pict/file_h_cre_link.jpg\" " + . "width=\"59\" " + . "alt=\"Create Time\">" + . "</td>\n" + . "<td>$sp</td>\n"; + } + + # Size + print " <td><img border=\"0\" " + . "src=\"pict/file_h_siz_link.jpg\" " + . "width=\"31\" height=20 " + . "alt=\"Size\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # UID + print " <td><img border=\"0\" " + . "src=\"pict/file_h_uid_link.jpg\" " + . "width=\"27\" height=20 " + . "alt=\"UID\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # GID + print " <td><img border=\"0\" " + . "src=\"pict/file_h_gid_link.jpg\" " + . "width=\"28\" height=20 " + . "alt=\"GID\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + # Meta + print " <td><img border=\"0\" " + . "src=\"pict/file_h_meta_link.jpg\" " + . "width=\"41\" height=20 " + . "alt=\"Meta\">" + . "</td>\n" + . "<td>$sp</td>\n"; + + my $row = 0; + while ($_ = Exec::read_pipe_line(*OUT)) { + + if ( +/^($::REG_MTYPE)\/($::REG_MTYPE)\s*(\*?)\s*($::REG_META)(\(realloc\))?:\t(.+?)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t($::REG_DATE)\t(\d+)\t(\d+)\t(\d+)$/o + ) + { + + # We have to remove the / from the beginning of the file name so + # save all values so they aren't lost + my $dt = $1; + my $it = $2; + my $d = $3; + my $i = $4; + my $r = 0; + $r = 1 if (defined $5); + my $n = $6; + my $m = $7; + my $a = $8; + my $c = $9; + my $cr = $10; + my $s = $11; + my $g = $12; + my $u = $13; + + if ($n =~ /^\/(.*)/) { + $n = $1; + } + my $enc_n = Args::url_encode($n); + my $iurl = +"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs&meta=$i"; + my $i_int = $i; + $i_int = $1 if ($i =~ /(\d+)-\d+-\d+/); + + if (($row % 2) == 0) { + print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR\">\n"; + } + else { + print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR_TABLE\">\n"; + } + + print "<td align=\"center\"><font color=\"$::DEL_COLOR[$r]\">" + . "$dt / $it</td>" + . "<td>$sp</td>\n"; + + if ($it eq 'd') { + my $url = + "$::PROGNAME?mod=$::MOD_FILE&" + . "view=$File::FILE_LIST&$Args::baseargs&meta=$i" + . "&sort=$sort&dir=$enc_n&dirmode=$dirmode"; + + print "<td>"; + if ($i_int >= $Fs::first_meta{$ftype}) { + print "<a href=\"$url\" target=\"_self\">"; + } + print "<font color=\"$::DEL_COLOR[$r]\"><tt>" + . Print::html_encode($mnt . $n) + . "</tt></td>" + . "<td>$sp</td>\n"; + } + else { + my $url = + "$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR&" + . "$Args::baseargs&meta=$i&sort=$sort&dir=$enc_n" + . "&recmode=$File::REC_YES"; + + print "<td>"; + if (($i_int >= $Fs::first_meta{$ftype}) && ($it eq 'r')) { + print "<a href=\"$url\" target=\"content\">"; + } + print "<font color=\"$::DEL_COLOR[$r]\"><tt>" + . Print::html_encode($mnt . $n) + . "</tt></td>" + . "<td>$sp</td>\n"; + } + + $m = "$1 $2" + if ($m =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $a = "$1 $2" + if ($a =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $c = "$1 $2" + if ($c =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + $cr = "$1 $2" + if ($cr =~ /($::REG_DAY\s+$::REG_TIME)\s+($::REG_ZONE2)/o); + + print "<td><font color=\"$::DEL_COLOR[$r]\">$m</td>" + . "<td>$sp</td>\n" + if ($Fs::has_mtime{$ftype}); + + print "<td><font color=\"$::DEL_COLOR[$r]\">$a</td>" + . "<td>$sp</td>\n"; + print "<td><font color=\"$::DEL_COLOR[$r]\">$c</td>" + . "<td>$sp</td>\n" + if ($Fs::has_ctime{$ftype}); + print "<td><font color=\"$::DEL_COLOR[$r]\">$cr</td>" + . "<td>$sp</td>\n" + if ($Fs::has_crtime{$ftype}); + + print "<td><font color=\"$::DEL_COLOR[$r]\">$s</td>" + . "<td>$sp</td>\n" + . "<td><font color=\"$::DEL_COLOR[$r]\">$g</td>" + . "<td>$sp</td>\n" + . "<td><font color=\"$::DEL_COLOR[$r]\">$u</td>" + . "<td>$sp</td>\n"; + + print "<td>"; + if ($i_int >= $Fs::first_meta{$ftype}) { + print "<a href=\"$iurl\" target=\"_top\">"; + } + print "<font color=\"$::DEL_COLOR[$r]\">$i</a>"; + print " (realloc)" if $r; + print "</td></tr>\n"; + } + else { + print "Error Parsing File (invalid characters?)<br>: $_\n<br>"; + } + + $row++; + } + close(OUT); + print "</table>\n"; + + print "<center>None</center>\n" + if ($row == 0); + + Print::print_html_footer(); + return 0; +} + +# Content Frame +# This creates two frames for the lower rhs frame +# +sub content_fr { + Print::print_html_header_frameset(""); + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + print "<frameset rows=\"65,*\">\n"; + + my $recmode = $File::REC_NO; + + if (exists $Args::enc_args{'recmode'}) { + $recmode = $Args::enc_args{'recmode'}; + } + else { + + # We need to get the allocation status of this structure + my $meta_int = $meta; + $meta_int = $1 if ($meta_int =~ /(\d+)-\d+(-\d+)?/); + + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ils' -f $ftype -e -o $offset -i $imgtype $img $meta_int" + ); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + next unless ($_ =~ /^$meta/); + if ($_ =~ /^$meta\|f/) { + $recmode = $File::REC_YES; + } + elsif ($_ =~ /^$meta\|a/) { + $recmode = $File::REC_NO; + } + else { + Print::print_check_err("Error parsing ils output: $_"); + } + } + } + close(OUT); + + # Get the file type so we can show the thumb nails automatically + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + + my $apptype = Exec::read_pipe_line(*OUT); + close(OUT); + + $apptype = "Error getting file type" + if ((!defined $apptype) || ($apptype eq "")); + + # The menu for the different viewing options + print "<frame src=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_MENU&" + . "$Args::baseargs&dir=$Args::enc_args{'dir'}" + . "&meta=$Args::enc_args{'meta'}&sort=$FIL_SORT_ASC&recmode=$recmode\">\n"; + + # Print the image thumbnail + if (($apptype =~ /image data/) || ($apptype =~ /PC bitmap data/)) { + print "<frame src=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_IMG" + . "&$Args::baseargs&dir=$Args::enc_args{'dir'}" + . "&meta=$Args::enc_args{'meta'}" + . "&sort=$FIL_SORT_ASC&recmode=$recmode\" name=\"cont2\">\n</frameset>"; + } + else { + + # Where the actual content will be displayed + print "<frame src=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT" + . "&$Args::baseargs&dir=$Args::enc_args{'dir'}" + . "&meta=$Args::enc_args{'meta'}" + . "&sort=$FIL_SORT_ASC&recmode=$recmode\" name=\"cont2\">\n</frameset>"; + } + + Print::print_html_footer_frameset(); + return 0; +} + +# This is the index for the lower rhs frame +# Choose the content display type here +sub content_menu { + Args::check_sort(); + Args::check_recmode(); + + Print::print_html_header(""); + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $recmode = Args::get_recmode(); + + # Get the file type + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + + my $apptype = Exec::read_pipe_line(*OUT); + close(OUT); + + $apptype = "Error getting file type" + if ((!defined $apptype) || ($apptype eq "")); + + # We already have the path in the content window below, so save space + # print "<center><tt>$mnt$Args::args{'dir'}</tt>\n"; + print "<center>\n"; + + my $url = + "&$Args::baseargs&dir=$Args::enc_args{'dir'}" + . "&meta=$Args::enc_args{'meta'}&recmode=$recmode"; + + # Print the options for output display + print "<table cellspacing=\"0\" cellpadding=\"2\">\n<tr>\n" + . "<td>ASCII (<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT&" + . "sort=$FIL_SORT_ASC$url\" target=\"cont2\">display</a> - " + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::REPORT&" + . "sort=$FIL_SORT_ASC$url\" target=\"_blank\">report</a>)</td>\n" + . "<td>*</td>\n" + . "<td>Hex (" + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT&" + . "sort=$FIL_SORT_HEX$url\" target=\"cont2\">display</a> - " + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::REPORT&" + . "sort=$FIL_SORT_HEX$url\" target=\"_blank\">report</a>)</td>\n" + . "<td>*</td>\n" + . "<td>ASCII Strings (" + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT&" + . "sort=$FIL_SORT_STR$url\" target=\"cont2\">display</a> - " + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::REPORT&" + . "sort=$FIL_SORT_STR$url\" target=\"_blank\">report</a>)</td>\n" + . "<td>*</td>\n" + . "<td><a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::EXPORT&$url\">" + . "Export</a></td>\n"; + + # if the file is either image or HTML, then let them view it + if ( ($apptype =~ /image data/) + || ($apptype =~ /PC bitmap data/)) + { + print "<td>*</td>\n<td><a href=\"$::PROGNAME?" + . "mod=$::MOD_FILE&view=$File::CONT_IMG$url\"" + . "target=\"cont2\">View</a></td>\n"; + } + elsif ($apptype =~ /HTML document text/) { + print "<td>*</td>\n<td><a href=\"$::PROGNAME?" + . "mod=$::MOD_APPVIEW&view=$Appview::CELL_FRAME$url\"" + . "target=\"_blank\">View</a></td>\n"; + } + + print "<td>*</td>\n" + . "<td><a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::ENTER_FILE$url\" target=\"_blank\">" + . "Add Note</a></td>\n" + if ($::USE_NOTES == 1); + + print "</tr></table>\n"; + + print "File Type: $apptype\n"; + print + "<br><font color=\"$::DEL_COLOR[0]\">Deleted File Recovery Mode</font>\n" + if ($recmode == $File::REC_YES); + print "</center>\n"; + + Print::print_html_footer(); + return 0; +} + +# +# Display the actual content here +# +# NOTE: This has a media type of raw text +# +sub content { + Args::check_sort(); + Args::check_recmode(); + + Print::print_text_header(); + + my $sort = Args::get_sort(); + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $recflag = ""; + $recflag = " -r " if (Args::get_recmode() == $File::REC_YES); + + my $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + + local *OUT; + if ($sort == $FIL_SORT_ASC) { + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Viewing $fname ($meta) as ASCII"); + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + + print "Contents Of File: $fname\n\n\n"; + Print::print_output($_) while ($_ = Exec::read_pipe_data(*OUT, 1024)); + close(OUT); + } + elsif ($sort == $FIL_SORT_HEX) { + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Viewing $fname ($meta) as Hex"); + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + + print "Hex Contents Of File: $fname\n\n\n"; + my $offset = 0; + while ($_ = Exec::read_pipe_data(*OUT, 1024)) { + Print::print_hexdump($_, $offset * 1024); + $offset++; + } + close(OUT); + } + elsif ($sort == $FIL_SORT_STR) { + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Viewing $fname ($meta) as strings"); + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::TSKDIR/srch_strings' -a" + ); + + print "ASCII String Contents Of File: $fname\n\n\n\n"; + Print::print_output($_) while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + } + + Print::print_text_footer(); + + return 0; +} + +sub content_img { + + Print::print_html_header("image content"); + + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + my $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + + my $url = + "&$Args::baseargs&meta=$Args::enc_args{'meta'}" + . "&dir=$Args::enc_args{'dir'}&" + . "cell_mode=2&recmode=$Args::enc_args{'recmode'}"; + + print "<tt>$fname</tt><br><br>\n" + . "<table><tr>\n" + . "<td width=250 align=\"center\">" + . "<b>Thumbnail:</b><br>" + . "<img src=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_CONT${url}\" width=\"200\"></td>\n" + . "<td valign=top>" + . "<a href=\"$::PROGNAME?mod=$::MOD_APPVIEW&view=$Appview::CELL_CONT${url}\" " + . "target=_blank>View Full Size Image</a><br>\n</td>\n" + . "</tr></table>\n"; + + Print::print_html_footer(); + + return 0; +} + +# Export the contents of a file +sub export { + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + + my $recflag = ""; + + $recflag = " -r " + if ( (exists $Args::enc_args{'recmode'}) + && ($Args::enc_args{'recmode'} == $File::REC_YES)); + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Saving contents of $fname ($meta)"); + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + + # We can't trust the mnt and dir values (since there + # could be bad ASCII values, so only allow basic chars into name + $fname =~ tr/a-zA-Z0-9\_\-\@\,/\./c; + $fname = $1 if ($fname =~ /^\.(.*)$/); + + Print::print_oct_header("$vol-${fname}"); + + print "$_" while ($_ = Exec::read_pipe_data(*OUT, 1024)); + + Print::print_oct_footer(); + + return 0; +} + +# Display a report for a file +# This is intended to have its own window +# +sub report { + Args::check_sort(); + + my $sort = Args::get_sort(); + my $vol = Args::get_vol('vol'); + my $meta = Args::get_meta('meta'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $type; + my $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + my $recflag = ""; + + $recflag = " -r " + if ( (exists $Args::enc_args{'recmode'}) + && ($Args::enc_args{'recmode'} == $File::REC_YES)); + + # We can't trust the mnt and dir values (since there + # could be bad ASCII values, so only allow basic chars into name + $fname =~ tr/a-zA-Z0-9\_\-\@\,/\./c; + $fname = $1 if ($fname =~ /^\.+(.*)$/); + $fname = $1 if ($fname =~ /^(.*?)\.+$/); + + Print::print_text_header("filename=$Args::args{'vol'}-${fname}.txt"); + + $fname = "$mnt$Args::args{'dir'}"; + if ($sort == $FIL_SORT_ASC) { + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Generating ASCII report for $fname ($meta)" + ); + $type = "ASCII"; + } + elsif ($sort == $FIL_SORT_HEX) { + Print::log_host_inv( + "$Args::args{'vol'}: Generating Hex report for $fname ($meta)"); + $type = "Hex"; + } + elsif ($sort == $FIL_SORT_STR) { + Print::log_host_inv( +"$Args::args{'vol'}: Generating ASCII strings report for $fname ($meta)" + ); + $type = "string"; + } + else { + print "\n\ninvalid sort value"; + return 1; + } + + # NOTE: There is a space in the beginning of the separator lines in + # order to make clear@stamper.itconsult.co.uk time stamping happy + # I think it confuses the lines that begin at the lhs with PGP + # headers and will remove the second line. + # + print " Autopsy $type Report\n\n" + . "-" x 70 . "\n" + . " GENERAL INFORMATION\n\n" + . "File: $fname\n"; + + # Calculate the MD5 value + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::MD5_EXE'" + ); + my $md5 = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5 = "Error getting MD5 Value" + if ((!defined $md5) || ($md5 eq "")); + + chomp $md5; + if ($recflag eq "") { + print "MD5 of file: $md5\n"; + } + else { + print "MD5 of recovered file: $md5\n"; + } + + if ($::SHA1_EXE ne "") { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::SHA1_EXE'" + ); + my $sha1 = Exec::read_pipe_line(*OUT); + close(OUT); + + $sha1 = "Error getting SHA-1 Value" + if ((!defined $sha1) || ($sha1 eq "")); + + chomp $sha1; + if ($recflag eq "") { + print "SHA-1 of file: $sha1\n"; + } + else { + print "SHA-1 of recovered file: $sha1\n"; + } + } + + if ($sort == $FIL_SORT_STR) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::TSKDIR/srch_strings' -a | '$::MD5_EXE'" + ); + $md5 = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5 = "Error getting MD5 Value" + if ((!defined $md5) || ($md5 eq "")); + + chomp $md5; + print "MD5 of ASCII strings: $md5\n"; + + if ($::SHA1_EXE ne "") { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::TSKDIR/srch_strings' -a | '$::SHA1_EXE'" + ); + $sha1 = Exec::read_pipe_line(*OUT); + close(OUT); + + $sha1 = "Error getting SHA-1 Value" + if ((!defined $sha1) || ($sha1 eq "")); + + chomp $sha1; + print "SHA-1 of ASCII strings: $sha1\n"; + } + } + + print "\nImage: $Caseman::vol2path{$vol}\n"; + if (($Caseman::vol2start{$vol} == 0) && ($Caseman::vol2end{$vol} == 0)) { + print "Offset: Full image\n"; + } + elsif ($Caseman::vol2end{$vol} == 0) { + print "Offset: $Caseman::vol2start{$vol} to end\n"; + } + else { + print "Offset: $Caseman::vol2start{$vol} to $Caseman::vol2end{$vol}\n"; + } + print "File System Type: $ftype\n"; + + my $date = localtime(); + print "\nDate Generated: $date\n" + . "Investigator: $Args::args{'inv'}\n\n" + . "-" x 70 . "\n" + . " META DATA INFORMATION\n\n"; + + # Get the meta details + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + print $_ while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + + # File Type + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + my $apptype = Exec::read_pipe_line(*OUT); + close(OUT); + + $apptype = "Error getting file type" + if ((!defined $apptype) || ($apptype eq "")); + + print "\nFile Type: $apptype"; + + print "\n" . "-" x 70 . "\n"; + if ($sort == $FIL_SORT_ASC) { + print " CONTENT (Non-ASCII data may not be shown)\n\n"; + } + else { + print " CONTENT\n\n"; + } + + if ($sort == $FIL_SORT_ASC) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + Print::print_output($_) while ($_ = Exec::read_pipe_data(*OUT, 1024)); + close(OUT); + } + elsif ($sort == $FIL_SORT_HEX) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta" + ); + my $offset = 0; + while ($_ = Exec::read_pipe_data(*OUT, 1024)) { + Print::print_hexdump($_, $offset * 1024); + $offset++; + } + close(OUT); + } + elsif ($sort == $FIL_SORT_STR) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype $recflag -o $offset -i $imgtype $img $meta | '$::TSKDIR/srch_strings' -a" + ); + Print::print_output($_) while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + } + + print "\n" + . "-" x 70 . "\n" + . " VERSION INFORMATION\n\n" + . "Autopsy Version: $::VER\n"; + print "The Sleuth Kit Version: " . ::get_tskver() . "\n"; + + Print::print_text_footer(); + return 0; +} + +# Generate the MD5 value for every file in a given directory and save +# them to a text file +sub md5list { + my $vol = Args::get_vol('vol'); + my $meta = Args::get_meta('meta'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $fname = "$mnt$Args::args{'dir'}"; + $fname = 'root' if ($fname eq '/'); + $fname =~ s/\/\//\//g; + + # We can't trust the mnt and dir values (since there + # could be bad ASCII values, so only allow basic chars into name + $fname =~ tr/a-zA-Z0-9\_\-\@\,/\./c; + + # remove .'s at beginning and end + $fname = $1 if ($fname =~ /^\.+(.*)$/); + $fname = $1 if ($fname =~ /^(.*?)\.+$/); + + Print::print_text_header("filename=$fname.md5"); + + $fname = "$mnt$Args::args{'dir'}"; + $fname =~ s/\/\//\//g; + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/fls' -f $ftype -Fu -o $offset -i $imgtype $img $meta"); + + print "MD5 Values for files in $fname ($Caseman::vol2sname{$vol})\n\n"; + + while ($_ = Exec::read_pipe_line(*OUT)) { + + # area for allocated files + if ( (/r\/[\w\-]\s+([\d\-]+):\s+(.*)$/) + || (/-\/r\s+([\d\-]+):\s+(.*)$/)) + { + my $in = $1; + my $name = $2; + + local *OUT_MD5; + Exec::exec_pipe(*OUT_MD5, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $in | '$::MD5_EXE'" + ); + my $md5out = Exec::read_pipe_line(*OUT_MD5); + + $md5out = "Error calculating MD5" + if ((!defined $md5out) || ($md5out eq "")); + + chomp $md5out; + print "$md5out\t" . Print::html_encode($name) . "\n"; + close(OUT_MD5); + } + elsif (/[\w\-]\/[\w\-]\s+([\d\-]+):\s+(.*)$/) { + + # ignore, non-file types such as sockets or symlinks that do not have + # MD5 values that make sense + } + + # Hmmmm + else { + print "Error parsing file (invalid characters?): $_\n"; + } + } + close(OUT); + + Print::print_text_footer(); + + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header(""); + print "<br><center><h3>File Browsing Mode</h3><br>\n" + . "<p>In this mode, you can view file and directory contents.</p>\n" + . "<p>File contents will be shown in this window.<br>\n" + . "More file details can be found using the Metadata link at the end of the list (on the right).<br>\n" + . "You can also sort the files using the column headers</p>\n"; + Print::print_html_footer(); + return 0; +} + diff --git a/lib/Filesystem.pm b/lib/Filesystem.pm new file mode 100644 index 0000000000000000000000000000000000000000..935a22b11be3faf6597de212ee6944a91d0031f3 --- /dev/null +++ b/lib/Filesystem.pm @@ -0,0 +1,170 @@ +# +# File system layer functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Updated 1/13 + +package Filesystem; + +$Filesystem::STATS = 0; + +sub main { + + # By default, show the main window + $Args::args{'view'} = $Args::enc_args{'view'} = $Filesystem::STATS + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the meta data address + if ($view == $Filesystem::STATS) { + return stats(); + } + else { + Print::print_check_err("Invalid File System View"); + } +} + +sub stats_disk { + Print::print_html_header("Disk Status"); + + my $vol = Args::get_vol('vol'); + my $img = $Caseman::vol2path{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $dtype = $Caseman::vol2dtype{$vol}; + + # Run 'mmls' on the image + Exec::exec_pipe(*OUT, + "'$::TSKDIR/mmls' -o $offset -i $imgtype -t $dtype -r $img"); + + # cycle through results and add each to table with file system type + print "<center><h3>Disk Image Details</h3></center>\n"; + print "<b>PARTITION INFORMATION</b><p>\n"; + + while ($_ = Exec::read_pipe_line(*OUT)) { + print "<tt>$_</tt><br>\n"; + } + return 0; + +} + +############ FILE SYSTEM ################## +sub stats { + + my $vol = Args::get_vol('vol'); + + return stats_disk() if ($Caseman::vol2cat{$vol} eq "disk"); + + Print::print_html_header("File System Status"); + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Displaying file system details"); + print "<center><h3>General File System Details</h3></center><p>\n"; + + my $fat = 0; + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/fsstat' -f $ftype -o $offset -i $imgtype $img"); + while ($_ = Exec::read_pipe_line(*OUT)) { + + if (/\-\-\-\-\-\-\-\-\-\-/) { + + # Ignore these and print them ahead of the headers + } + + # need the space to prevent NTFS STD_INFORMATION from triggering it + elsif (/ INFORMATION/) { + print "<hr><b>$_</b><p>\n"; + } + elsif (($ftype =~ /fat/) && ($_ =~ /FAT CONTENTS/)) { + print "<hr><b>$_</b><p>\n"; + + # Set the flag if we reach the FAT + $fat = 1; + } + + # Special case for FAT + # We will be giving hyperlinks in the FAT table dump + elsif ($fat == 1) { + + # Ignore the divider + if (/\-\-\-\-\-\-\-\-\-\-/) { + print "$_<br>"; + next; + } + + if (/^((\d+)\-\d+\s+\((\d+)\)) \-\> ([\w]+)$/) { + my $full = $1; + my $blk = $2; + my $len = $3; + my $next = $4; + + # Print the tag so that other FAT entries can link to it + print "<a name=\"$blk\">\n"; + + print +"<a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_FRAME&submod=$::MOD_DATA&" + . "block=$blk&len=$len\" target=\"_top\">$full</a> -> "; + + if ($next eq 'EOF') { + print "EOF<br>\n"; + } + else { + print "<a href=\"#$next\">$next</a><br>\n"; + } + } + else { + $fat = 0; + print "$_<br>"; + } + } + else { + print "$_<br>"; + } + } + close(OUT); + + Print::print_html_footer(); + return 0; +} + +1; diff --git a/lib/Frame.pm b/lib/Frame.pm new file mode 100644 index 0000000000000000000000000000000000000000..20e107f45741cc65956ffe58b3ed0edd685fd072 --- /dev/null +++ b/lib/Frame.pm @@ -0,0 +1,336 @@ +# +# Functions to create the tabs and frame of the main browsing mode +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Frame; + +$Frame::IMG_FRAME = 0; +$Frame::IMG_TABS = 1; +$Frame::IMG_BLANK = 2; + +sub main { + Args::check_vol('vol'); + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Frame::IMG_FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view == $Frame::IMG_FRAME) { + vol_browse_frame(); + } + elsif ($view == $Frame::IMG_TABS) { + vol_browse_tabs(); + } + elsif ($view == $Frame::IMG_BLANK) { + vol_browse_blank(); + } + else { + Print::print_check_err("Invalid Frame View"); + } + + return 0; +} + +# create the frame for the tabs on top and the generic message on the bottom +sub vol_browse_frame { + Print::print_html_header_frameset( + "$Args::args{'case'}:$Args::args{'host'}:$Args::args{'vol'}"); + + my $submod = $::MOD_FRAME; + $submod = Args::get_submod() if (exists $Args::args{'submod'}); + + # Print the rest of the frames + my $str = ""; + my $view = ""; + + if ($submod == $::MOD_FILE) { + $str .= "&meta=$Args::args{'meta'}" if (exists $Args::args{'meta'}); + $str .= "&dir=$Args::args{'dir'}" if (exists $Args::args{'dir'}); + $str .= "&sort=$Args::args{'sort'}" if (exists $Args::args{'sort'}); + $str .= "&dmode=$Args::args{'dmode'}" if (exists $Args::args{'dmode'}); + } + elsif ($submod == $::MOD_DATA) { + $str .= "&block=$Args::args{'block'}" if (exists $Args::args{'block'}); + $str .= "&len=$Args::args{'len'}" if (exists $Args::args{'len'}); + } + elsif ($submod == $::MOD_META) { + $str .= "&meta=$Args::args{'meta'}" if (exists $Args::args{'meta'}); + } + elsif ($submod == $::MOD_FRAME) { + $view = "&view=$Frame::IMG_BLANK"; + } + + print <<EOF; + +<frameset rows=\"40,*\"> + <frame src=\"$::PROGNAME?mod=$::MOD_FRAME&view=$Frame::IMG_TABS&$Args::baseargs&submod=$submod\"> + + <frame src=\"$::PROGNAME?mod=$submod$view&$Args::baseargs$str\"> +</frameset> + +<NOFRAMES> + <center> + Autopsy requires a browser that supports frames. + </center> +</NOFRAMES> + +EOF + + Print::print_html_footer_frameset(); + return 0; +} + +# Display a message when the image is opened (below the tabs) +sub vol_browse_blank { + Print::print_html_header("Main Message"); + + print <<EOF; + +<center> + <br><br><br><br><br><br><br> + To start analyzing this volume, choose an analysis mode from the tabs above. +</center> + +EOF + Print::print_html_footer(); + return 0; +} + +sub vol_browse_tabs { + Args::check_submod(); + Print::print_html_header_tabs("Mode Tabs"); + + my $submod = Args::get_submod(); + my $vol = Args::get_vol('vol'); + + my $special = 0; + $special = 1 + unless (($Caseman::vol2cat{$vol} eq "part") + && ($Fs::is_fs{$Caseman::vol2ftype{$vol}} == 1)); + + # if ( ($Caseman::vol2ftype{$vol} eq "strings") + # || ($Caseman::vol2ftype{$vol} eq "blkls") + # || ($Caseman::vol2ftype{$vol} eq "swap") + # || ($Caseman::vol2ftype{$vol} eq "raw")); + + print "<center><table width=\"800\" border=\"0\" cellspacing=\"0\" " + . "cellpadding=\"0\"><tr>\n"; + + # Files + print "<td align=\"center\" width=116>"; + if ($special == 0) { + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_FILE&$Args::baseargs\"" + . "target=\"_top\">"; + + # Current + if ($submod == $::MOD_FILE) { + print "<img border=0 " + . "src=\"pict/main_t_fil_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"File Analysis (Current Mode)\"></a>"; + } + + # Link + else { + print "<img border=0 " + . "src=\"pict/main_t_fil_link.jpg\" " + . "width=116 height=38 " + . "alt=\"File Analysis\"></a>"; + } + } + + # non-link + else { + print "<img border=0 " + . "src=\"pict/main_t_fil_org.jpg\" " + . "width=116 height=38 " + . "alt=\"File Analysis (not available)\">"; + } + + # Search + print "</td>\n" . "<td align=\"center\" width=116>"; + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_KWSRCH&$Args::baseargs\"" + . " target=\"_top\">"; + + if ($submod == $::MOD_KWSRCH) { + print "<img border=0 " + . "src=\"pict/main_t_srch_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"Keyword Search Mode (Current Mode)\"></a>"; + } + else { + print "<img border=0 " + . "src=\"pict/main_t_srch_link.jpg\" " + . "width=116 height=38 " + . "alt=\"Keyword Search Mode\"></a>"; + } + + # File Type + print "</td>\n" . "<td align=\"center\" width=116>"; + + if (($special == 0) && ($::LIVE == 0)) { + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_APPSORT&$Args::baseargs\"" + . " target=\"_top\">"; + + # Current + if ($submod == $::MOD_APPSORT) { + print "<img border=0 " + . "src=\"pict/main_t_ftype_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"File Type (Current Mode)\"></a>"; + } + else { + print "<img border=0 " + . "src=\"pict/main_t_ftype_link.jpg\" " + . "width=116 height=38 " + . "alt=\"File Type\"></a>"; + } + } + else { + print "<img border=0 " + . "src=\"pict/main_t_ftype_org.jpg\" " + . "width=116 height=38 " + . "alt=\"File Type (not available)\">"; + } + + # Image Details + print "</td>\n" . "<td align=\"center\" width=116>"; + + if (($special == 0) || ($Caseman::vol2cat{$vol} eq "disk")) { + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_FS&$Args::baseargs\"" + . " target=\"_top\">"; + + if ($submod == $::MOD_FS) { + print "<img border=0 " + . "src=\"pict/main_t_img_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"Image Details Mode (Current Mode)\"></a>"; + } + else { + print "<img border=0 " + . "src=\"pict/main_t_img_link.jpg\" " + . "width=116 height=38 " + . "alt=\"Image Details Mode\"></a>"; + } + } + else { + print "<img border=0 " + . "src=\"pict/main_t_img_org.jpg\" " + . "width=116 height=38 " + . "alt=\"Image Details Mode (not available)\">"; + } + + # Meta Data + print "</td>\n" . "<td align=\"center\" width=116>"; + + if ($special == 0) { + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_META&$Args::baseargs\"" + . " target=\"_top\">"; + + if ($submod == $::MOD_META) { + print "<img border=0 " + . "src=\"pict/main_t_met_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"Meta Data Mode (Current Mode)\"></a>"; + } + else { + print "<img border=0 " + . "src=\"pict/main_t_met_link.jpg\" " + . "width=116 height=38 " + . "alt=\"Meta Data Mode\"></a>"; + } + } + else { + print "<img border=0 " + . "src=\"pict/main_t_met_org.jpg\" " + . "width=116 height=38 " + . "alt=\"Meta Data Mode (not available)\">"; + } + + # Data Units + print "</td>\n" . "<td align=\"center\" width=116>"; + + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_DATA&$Args::baseargs\"" + . " target=\"_top\">"; + + # Current + if ($submod == $::MOD_DATA) { + print "<img border=0 " + . "src=\"pict/main_t_dat_cur.jpg\" " + . "width=116 height=38 " + . "alt=\"Data Units Mode (Current Mode)\"></a>"; + } + + # Link + else { + print "<img border=0 " + . "src=\"pict/main_t_dat_link.jpg\" " + . "width=116 height=38 " + . "alt=\"Data Units Mode\"></a>"; + } + + # Help - set to current mode + print "</td>\n" + . "<td align=\"center\" width=52>" + . "<a href=\"$::HELP_URL\"" + . " target=\"_blank\">" + . "<img border=0 " + . "src=\"pict/tab_help.jpg\" " + . "width=52 " + . "alt=\"Help\">" + . "</a></td>\n"; + + # Close + print "<td align=\"center\" width=52>" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_OPEN&$Args::baseargs_novol\" target=\"_top\">" + . "<img border=0 src=\"pict/tab_close.jpg\" width=52 " + . "alt=\"Close Image\"></a></td>\n"; + + print "</tr></table>\n"; + + Print::print_html_footer_tabs(); + return 0; +} diff --git a/lib/Fs.pm b/lib/Fs.pm new file mode 100644 index 0000000000000000000000000000000000000000..cc188012b7be1661057590b6abb2307fb2707975 --- /dev/null +++ b/lib/Fs.pm @@ -0,0 +1,171 @@ +# +package Fs; + +$Fs::types[0] = "ext"; +$Fs::types[1] = "fat"; +$Fs::types[2] = "ntfs"; +$Fs::types[3] = "ufs"; +$Fs::types[4] = "iso9660"; +$Fs::types[5] = "-----"; +$Fs::types[6] = "fat12"; +$Fs::types[7] = "fat16"; +$Fs::types[8] = "fat32"; +$Fs::types[9] = "bsdi"; +$Fs::types[10] = "freebsd"; +$Fs::types[11] = "openbsd"; +$Fs::types[12] = "solaris"; +$Fs::types[13] = "hfs"; + +# These need to be updated as The Sleuth Kit supports more file systems +# +# addr_unit contains the addressable unit per filesystem type +# first_meta contains the first usable meta address on a system +# root_meta is the meta address for the root directory (diff than +# first on ntfs) + +$Fs::addr_unit{'disk'} = 'Sector'; +$Fs::first_addr{'disk'} = 0; +$Fs::is_fs{'disk'} = 0; + +$Fs::addr_unit{'blkls'} = 'Unit'; +$Fs::first_addr{'blkls'} = 0; +$Fs::is_fs{'blkls'} = 0; + +# raw +$Fs::addr_unit{'raw'} = 'Unit'; +$Fs::first_addr{'raw'} = 0; +$Fs::is_fs{'raw'} = 0; + +# Swap +$Fs::addr_unit{'swap'} = 'Unit'; +$Fs::first_addr{'swap'} = 0; +$Fs::is_fs{'swap'} = 0; + +# BSDI +$Fs::first_meta{'bsdi'} = $Fs::root_meta{'bsdi'} = 2; +$Fs::first_addr{'bsdi'} = 0; +$Fs::addr_unit{'bsdi'} = 'Fragment'; +$Fs::has_ctime{'bsdi'} = 1; +$Fs::has_crtime{'bsdi'} = 0; +$Fs::has_mtime{'bsdi'} = 1; +$Fs::meta_str{'bsdi'} = "Inode"; +$Fs::is_fs{'bsdi'} = 1; + +# FreeBSD +$Fs::first_meta{'freebsd'} = $Fs::root_meta{'freebsd'} = 2; +$Fs::first_addr{'freebsd'} = 0; +$Fs::addr_unit{'freebsd'} = 'Fragment'; +$Fs::has_ctime{'freebsd'} = 1; +$Fs::has_crtime{'freebsd'} = 0; +$Fs::has_mtime{'freebsd'} = 1; +$Fs::meta_str{'freebsd'} = "Inode"; +$Fs::is_fs{'freebsd'} = 1; + +# OpenBSD +$Fs::first_meta{'openbsd'} = $Fs::root_meta{'openbsd'} = 2; +$Fs::first_addr{'openbsd'} = 0; +$Fs::addr_unit{'openbsd'} = 'Fragment'; +$Fs::has_ctime{'openbsd'} = 1; +$Fs::has_crtime{'openbsd'} = 0; +$Fs::has_mtime{'openbsd'} = 1; +$Fs::meta_str{'openbsd'} = "Inode"; +$Fs::is_fs{'openbsd'} = 1; + +# Solaris +$Fs::first_meta{'solaris'} = $Fs::root_meta{'solaris'} = 2; +$Fs::first_addr{'solaris'} = 0; +$Fs::addr_unit{'solaris'} = 'Fragment'; +$Fs::has_ctime{'solaris'} = 1; +$Fs::has_crtime{'solaris'} = 0; +$Fs::has_mtime{'solaris'} = 1; +$Fs::meta_str{'solaris'} = "Inode"; +$Fs::is_fs{'solaris'} = 1; + +# UFS +$Fs::first_meta{'ufs'} = $Fs::root_meta{'ufs'} = 2; +$Fs::first_addr{'ufs'} = 0; +$Fs::addr_unit{'ufs'} = 'Fragment'; +$Fs::has_ctime{'ufs'} = 1; +$Fs::has_crtime{'ufs'} = 0; +$Fs::has_mtime{'ufs'} = 1; +$Fs::meta_str{'ufs'} = "Inode"; +$Fs::is_fs{'ufs'} = 1; + +# Linux +$Fs::first_meta{'linux-ext2'} = $Fs::root_meta{'linux-ext2'} = 2; +$Fs::first_addr{'linux-ext2'} = 0; +$Fs::addr_unit{'linux-ext2'} = 'Fragment'; +$Fs::has_ctime{'linux-ext2'} = 1; +$Fs::has_crtime{'linux-ext2'} = 0; +$Fs::has_mtime{'linux-ext2'} = 1; +$Fs::meta_str{'linux-ext2'} = "Inode"; +$Fs::is_fs{'linux-ext2'} = 1; + +$Fs::first_meta{'linux-ext3'} = $Fs::root_meta{'linux-ext3'} = 2; +$Fs::first_addr{'linux-ext3'} = 0; +$Fs::addr_unit{'linux-ext3'} = 'Fragment'; +$Fs::has_ctime{'linux-ext3'} = 1; +$Fs::has_crtime{'linux-ext3'} = 0; +$Fs::has_mtime{'linux-ext3'} = 1; +$Fs::meta_str{'linux-ext3'} = "Inode"; +$Fs::is_fs{'linux-ext3'} = 1; + +$Fs::first_meta{'ext'} = $Fs::root_meta{'ext'} = 2; +$Fs::first_addr{'ext'} = 0; +$Fs::addr_unit{'ext'} = 'Fragment'; +$Fs::has_ctime{'ext'} = 1; +$Fs::has_crtime{'ext'} = 0; +$Fs::has_mtime{'ext'} = 1; +$Fs::meta_str{'ext'} = "Inode"; +$Fs::is_fs{'ext'} = 1; + +# FAT +$Fs::first_meta{'fat'} = $Fs::first_meta{'fat12'} = $Fs::first_meta{'fat16'} = + $Fs::first_meta{'fat32'} = 1; +$Fs::root_meta{'fat'} = $Fs::root_meta{'fat12'} = $Fs::root_meta{'fat16'} = + $Fs::root_meta{'fat32'} = 2; +$Fs::first_addr{'fat'} = $Fs::first_addr{'fat12'} = $Fs::first_addr{'fat16'} = + $Fs::first_addr{'fat32'} = 0; +$Fs::addr_unit{'fat'} = $Fs::addr_unit{'fat12'} = $Fs::addr_unit{'fat16'} = + $Fs::addr_unit{'fat32'} = 'Sector'; +$Fs::has_ctime{'fat'} = $Fs::has_ctime{'fat12'} = $Fs::has_ctime{'fat16'} = + $Fs::has_ctime{'fat32'} = 0; +$Fs::has_crtime{'fat'} = $Fs::has_crtime{'fat12'} = $Fs::has_crtime{'fat16'} = + $Fs::has_crtime{'fat32'} = 1; +$Fs::has_mtime{'fat'} = $Fs::has_mtime{'fat12'} = $Fs::has_mtime{'fat16'} = + $Fs::has_mtime{'fat32'} = 1; +$Fs::meta_str{'fat'} = $Fs::meta_str{'fat12'} = $Fs::meta_str{'fat16'} = + $Fs::meta_str{'fat32'} = "Dir Entry"; +$Fs::is_fs{'fat'} = $Fs::is_fs{'fat12'} = $Fs::is_fs{'fat16'} = + $Fs::is_fs{'fat32'} = 1; + +# NTFS +$Fs::first_meta{'ntfs'} = 0; +$Fs::root_meta{'ntfs'} = 5; +$Fs::first_addr{'ntfs'} = 0; +$Fs::addr_unit{'ntfs'} = 'Cluster'; +$Fs::has_ctime{'ntfs'} = 1; +$Fs::has_crtime{'ntfs'} = 1; +$Fs::has_mtime{'ntfs'} = 1; +$Fs::meta_str{'ntfs'} = "MFT Entry"; +$Fs::is_fs{'ntfs'} = 1; + +# ISO9660 +$Fs::first_meta{'iso9660'} = $Fs::root_meta{'iso9660'} = 0; +$Fs::first_addr{'iso9660'} = 0; +$Fs::addr_unit{'iso9660'} = 'Block'; +$Fs::has_ctime{'iso9660'} = 0; +$Fs::has_crtime{'iso9660'} = 1; +$Fs::has_mtime{'iso9660'} = 0; +$Fs::meta_str{'iso9660'} = "Directory Entry"; +$Fs::is_fs{'iso9660'} = 1; + +# HFS +$Fs::first_meta{'hfs'} = $Fs::root_meta{'hfs'} = 2; +$Fs::first_addr{'hfs'} = 0; +$Fs::addr_unit{'hfs'} = 'Block'; +$Fs::has_ctime{'hfs'} = 1; +$Fs::has_crtime{'hfs'} = 1; +$Fs::has_mtime{'hfs'} = 1; +$Fs::meta_str{'hfs'} = "Record"; +$Fs::is_fs{'hfs'} = 1; diff --git a/lib/Hash.pm b/lib/Hash.pm new file mode 100644 index 0000000000000000000000000000000000000000..640e38548e0da1e2d27c783e5f52eac6f143bc09 --- /dev/null +++ b/lib/Hash.pm @@ -0,0 +1,947 @@ +# +# Hash database and calculation functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2008 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Hash; + +$Hash::DB_MANAGER = 1; +$Hash::DB_LOOKUP = 2; +$Hash::DB_INDEX = 3; +$Hash::IMG_VERIFY = 4; +$Hash::IMG_CALC = 5; +$Hash::IMG_LIST_FR = 6; +$Hash::IMG_LIST = 7; +$Hash::BLANK = 8; + +sub main { + + return if ($::LIVE == 1); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view == $Hash::BLANK) { + blank(); + return 0; + } + elsif ($view == $Hash::DB_MANAGER) { + return db_manager(); + } + elsif ($view == $Hash::DB_LOOKUP) { + return db_lookup(); + } + elsif ($view == $Hash::DB_INDEX) { + return db_index(); + } + elsif ($view == $Hash::IMG_LIST_FR) { + return img_list_fr(); + } + elsif ($view == $Hash::IMG_LIST) { + return img_list(); + } + + Args::check_vol('vol'); + if ($view == $Hash::IMG_CALC) { + return img_calc(); + } + elsif ($view == $Hash::IMG_VERIFY) { + return img_verify(); + } + else { + Print::print_check_err("Invalid Hash View"); + } + +} + +sub index_md5sum { + my $db = shift; + local *OUT; + Exec::exec_pipe(*OUT, "'$::TSKDIR/hfind' -i md5sum '$db'"); + while ($_ = Exec::read_pipe_line(*OUT)) { + print "$_<br>\n"; + } + close(OUT); +} + +sub index_nsrl { + local *OUT; + Exec::exec_pipe(*OUT, "'$::TSKDIR/hfind' -i nsrl-md5 '$::NSRLDB'"); + while ($_ = Exec::read_pipe_line(*OUT)) { + print "$_<br>\n"; + } + close(OUT); +} + +# Manager/status Window from HOST Manager +sub db_manager { + Print::print_html_header("Hash Database Manager"); + + print <<EOF; + +Hash databases allow Autopsy to quickly identify known files. This includes +files that are known to be good and those that are known to be bad. The +'hfind' tool is used to lookup entries in the databases and it needs an +index file for each database. This window allows one to re-index the +database after it has been updated. + +<p> +To edit the location of the databases, you must manually edit the +<tt>host.aut</tt> file in the host directory. + +<hr> +<center> +<img src=\"pict/hashdb_h_alert.jpg\" alt=\"Alert Database\" border=\"0\"> +</center> +<p><b>Overview</b><br> +These files are known to be <U>bad</U> and are the ones that you want to +know about if they are in the image you are analyzing. For example, +this database would include hashes of known attacker tools, rootkits, +or photographs. + +EOF + print "<p><b>Details</b><br>\n"; + if ($Caseman::alert_db eq "") { + print "Location: <tt>Not Configured</tt><br>\n"; + } + elsif (-e "$Caseman::alert_db") { + print "Location: <tt>$Caseman::alert_db</tt><br>\n"; + if (-e "$Caseman::alert_db" . "-md5.idx") { + print "Status: MD5 Index File Exists<br>\n"; + } + else { + print "Status: Database has not been MD5 indexed<br>\n"; + } + + # Index Button + print "<p><a href=\"$::PROGNAME?mod=$::MOD_HASH&" + . "view=$Hash::DB_INDEX&hash_alert=1&$Args::baseargs\">" + . "<img src=\"pict/but_indexdb.jpg\" alt=\"Index DB\" " + . "width=116 height=20 border=\"0\">" + . "</a>\n"; + + # Lookup Button + if (-e "$Caseman::alert_db" . "-md5.idx") { + print "<p><b>Lookup</b><br>" + . "<form action=\"$::PROGNAME\" method=\"get\">" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::DB_LOOKUP\">\n" + . "<input type=\"hidden\" name=\"hash_alert\" value=\"1\">\n" + . Args::make_hidden() + . "<table cellspacing=\"10\" cellpadding=\"2\">\n<tr>\n" + . "<td align=\"left\">Enter MD5 Value: " + . "<input type=\"text\" name=\"md5\" size=40 maxlength=32></td>\n" + . "<td align=\"left\">" + . "<input type=\"image\" src=\"pict/but_lookup.jpg\" alt=\"Ok\" " + . "width=116 height=20 border=\"0\">\n" + . "</td></tr>\n</table>\n" + . "</form>"; + } + } + else { + print "Location: <tt>$Caseman::alert_db</tt><br>\n" + . "ERROR: Database not found<br>\n"; + } + + print <<EOF2; +<hr> +<center> +<img src=\"pict/hashdb_h_ig.jpg\" alt=\"Ignore Database\" border=\"0\"> +</center> +<p><b>Overview</b><br> +These files are known to be <U>good</U> and are the ones that you +can ignore if they are found in the image you are analyzing. For +example, this database would include hashes of known system binaries +and other documents that you do not want to waste time on when running +'sorter' or files that you want to confirm were not modified by an +attacker. + +EOF2 + + print "<p><b>Details</b><br>\n"; + if ($Caseman::exclude_db eq "") { + print "Location: <tt>Not Configured</tt><br>\n"; + } + elsif (-e "$Caseman::exclude_db") { + print "Location: <tt>$Caseman::exclude_db</tt><br>\n"; + if (-e "$Caseman::exclude_db" . "-md5.idx") { + print "Status: MD5 Index File Exists<br>\n"; + } + else { + print "Status: Database has not been MD5 indexed<br>\n"; + } + + # Index Button + print "<p><a href=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::DB_INDEX&" + . "hash_exclude=1&$Args::baseargs\">" + . "<img src=\"pict/but_indexdb.jpg\" alt=\"Index DB\" " + . "width=116 height=20 border=\"0\">" + . "</a>\n"; + + # Lookup Button + if (-e "$Caseman::exclude_db" . "-md5.idx") { + print "<p><b>Lookup</b><br>" + . "<form action=\"$::PROGNAME\" method=\"get\">" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::DB_LOOKUP\">\n" + . "<input type=\"hidden\" name=\"hash_exclude\" value=\"1\">\n" + . Args::make_hidden() + . "<table cellspacing=\"10\" cellpadding=\"2\">\n<tr>\n" + . "<td align=\"left\">Enter MD5 Value: " + . "<input type=\"text\" name=\"md5\" size=40 maxlength=32></td>\n" + . "<td align=\"left\">" + . "<input type=\"image\" src=\"pict/but_lookup.jpg\" alt=\"Ok\" " + . "width=116 height=20 border=\"0\">\n" + . "</td></tr>\n</table>\n" + . "</form>"; + } + } + else { + print "Location: <tt>$Caseman::exclude_db</tt><br>\n" + . "ERROR: Database not found<br>\n"; + } + + print <<EOF3; +<hr> +<center> +<img src=\"pict/hashdb_h_nsrl.jpg\" alt=\"NSRL Database\" border=\"0\"> +</center> +<p><b>Overview</b><br> +These files are known to be <U>good</U> and <U>bad</U>. It is currently +difficult to distinguish between known good and known bad, but the NSRL +is used in Autopsy to ignore all known files. + +EOF3 + + print "<p><b>Details</b><br>\n"; + if ($::NSRLDB eq "") { + print "Location: <tt>Not Configured</tt><br>\n"; + } + elsif (-e "$::NSRLDB") { + print "Location: <tt>$::NSRLDB</tt><br>\n"; + if (-e "$::NSRLDB" . "-md5.idx") { + print "Status: MD5 Index File Exists<br>\n"; + } + else { + print "Status: Database has not been MD5 indexed<br>\n"; + } + + # Index Button + print "<p><a href=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::DB_INDEX&" + . "hash_nsrl=1&$Args::baseargs\">" + . "<img src=\"pict/but_indexdb.jpg\" alt=\"Index DB\" " + . "width=116 height=20 border=\"0\">" + . "</a>\n"; + + # Lookup Button + if (-e "$::NSRLDB" . "-md5.idx") { + print "<p><b>Lookup</b><br>" + . "<form action=\"$::PROGNAME\" method=\"get\">" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::DB_LOOKUP\">\n" + . "<input type=\"hidden\" name=\"hash_nsrl\" value=\"1\">\n" + . Args::make_hidden() + . "<table cellspacing=\"10\" cellpadding=\"2\">\n<tr>\n" + . "<td align=\"left\">Enter MD5 Value: " + . "<input type=\"text\" name=\"md5\" size=40 maxlength=32></td>\n" + . "<td align=\"left\">" + . "<input type=\"image\" src=\"pict/but_lookup.jpg\" " + . "alt=\"Lookup\" width=116 height=20 border=0>\n" + . "</td></tr>\n</table>\n" + . "</form>"; + } + } + else { + print "Location: <tt>$::NSRLDB</tt><br>\n" + . "ERROR: Database not found<br>\n"; + } + + print <<EOF4; + +<hr><center> +<table width=600 cellspacing=\"0\" cellpadding=\"2\"> +<tr> + <td align=center> + <a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::VOL_OPEN&$Args::baseargs\"> + <img src=\"pict/menu_b_close.jpg\" alt=\"Close\" width=\"167\" height=20 border=\"0\"> + </a> + </td> + <td align=center> + <a href=\"$::HELP_URL\" target=\"_blank\"> + <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" + width=\"167\" height=20 border=0> + </a> + </td> +</tr> +</table> +EOF4 + + Print::print_html_footer(); + return 0; +} + +sub db_index { + Print::print_html_header("Hash Database Indexing"); + + if ( (exists $Args::args{'hash_exclude'}) + && ($Args::args{'hash_exclude'} == 1) + && ($Caseman::exclude_db ne "")) + { + Print::log_host_info("Exclude Database Re-Indexed"); + print "<hr><b>Exclude Database Indexing</b><p>\n"; + index_md5sum($Caseman::exclude_db); + } + + if ( (exists $Args::args{'hash_alert'}) + && ($Args::args{'hash_alert'} == 1) + && ($Caseman::alert_db ne "")) + { + Print::log_host_info("Alert Database Re-Indexed"); + print "<hr><b>Alert Database Indexing</b><p>\n"; + index_md5sum($Caseman::alert_db); + } + + if ( (exists $Args::args{'hash_nsrl'}) + && ($Args::args{'hash_nsrl'} == 1) + && ($::NSRLDB ne "")) + { + Print::log_host_info("NSRL Database Re-Indexed"); + print "<hr><b>NSRL Database Indexing</b><p>\n"; + index_nsrl(); + } + + print "<p>Indexing Complete<br>\n" + . "<hr><p>\n<a href=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::DB_MANAGER&" + . "$Args::baseargs\">\n" + . "<img src=\"pict/menu_b_hashdb.jpg\" width=\"167\" " + . "height=20 alt=\"Hash Databases\" border=\"0\"></a>\n"; + + Print::print_html_footer(); + return 0; +} + +# Lookup hashes in database +sub db_lookup { + Print::print_html_header("Hash Database Lookup"); + + unless ((exists $Args::args{'md5'}) + && ($Args::args{'md5'} =~ /^$::REG_MD5$/o)) + { + Print::print_err("Invalid MD5 Argument"); + } + + if ( (exists $Args::args{'hash_nsrl'}) + && ($Args::args{'hash_nsrl'} == 1) + && ($::NSRLDB ne "")) + { + print "<hr><b>NSRL Lookup</b><p>\n"; + + if (-e "$::NSRLDB") { + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/hfind' '$::NSRLDB' $Args::args{'md5'}"); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + Print::log_host_inv("NSRL Lookup ($Args::args{'md5'})"); + } + else { + print "NSRL Database Missing<br>\n"; + Print::log_host_inv( + "NSRL Lookup ($Args::args{'md5'}) - Database Missing"); + } + } + + if ( (exists $Args::args{'hash_exclude'}) + && ($Args::args{'hash_exclude'} == 1) + && ($Caseman::exclude_db ne "")) + { + print "<hr><b>Exclude Database Lookup</b><p>\n"; + + if (-e "$Caseman::exclude_db") { + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/hfind' '$Caseman::exclude_db' $Args::args{'md5'}"); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + Print::log_host_inv("Exclude Database Lookup ($Args::args{'md5'})"); + } + else { + print "Exclude Database Missing<br>\n"; + Print::log_host_inv( +"Exclude Database Lookup ($Args::args{'md5'}) - Database Missing" + ); + } + } + + if ( (exists $Args::args{'hash_alert'}) + && ($Args::args{'hash_alert'} == 1) + && ($Caseman::alert_db ne "")) + { + print "<hr><b>Alert Database Lookup</b><p>\n"; + + if (-e "$Caseman::alert_db") { + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/hfind' '$Caseman::alert_db' $Args::args{'md5'}"); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + Print::log_host_inv("Alert Database Lookup ($Args::args{'md5'})"); + } + else { + print "Alert Database Missing<br>\n"; + Print::log_host_inv( + "Alert Database Lookup ($Args::args{'md5'}) - Database Missing" + ); + } + } + + print "<hr><p>\n" + . "If any of the hash databases need to be re-indexed, use the " + . "<U>Hash Database Manager</U><p>" + . "<a href=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::DB_MANAGER&" + . "$Args::baseargs\" target=\"_top\">\n" + . "<img src=\"pict/menu_b_hashdb.jpg\" width=\"167\" " + . "height=20 alt=\"Hash Databases\" border=\"0\"></a>\n"; + + Print::print_html_footer(); + return 0; +} + +############ INTEGRITY CHECKS ################## + +# Special view for printing integrity check menu +# We show any file that we have a reference for + +# pass the md5 hash (from md5.txt) and then the sorted array +sub int_menu_print { + my %md5s = %{$_[0]}; + my @sort = @{$_[1]}; + + for (my $i = 0; $i <= $#sort; $i++) { + + print +"<tr><td align=\"right\"><tt><b>$Caseman::vol2sname{$sort[$i]}</b></tt></td>\n"; + + # It already exists, so make verify button + if (exists $md5s{$sort[$i]}) { + print "<td><tt>$md5s{$sort[$i]}</tt></td><td>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"cont\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$sort[$i]\">\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::IMG_VERIFY\">\n" + . "<input type=\"image\" src=\"pict/int_b_valid.jpg\" " + . "alt=\"Validate\" border=\"0\">\n" + . "</form></td></tr>\n"; + } + + # we currenly only support integrity for raw and split image formats + elsif (($Caseman::vol2itype{$sort[$i]} ne "raw") + && ($Caseman::vol2itype{$sort[$i]} ne "split")) + { + print +"<td colspan=2>Integrity checks for image type $Caseman::vol2itype{$sort[$i]} not yet supported</td></tr>\n"; + } + + # Generate New button + else { + print "<td> </td><td>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"cont\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$sort[$i]\">\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::IMG_CALC\">\n" + . "<input type=\"image\" src=\"pict/int_b_calc.jpg\" " + . "alt=\"Calculate\" border=\"0\">\n" + . "</form></td></tr>\n"; + } + } + + return; +} + +# Create a frame with two rows, one with the list of images to check +# and then the bottom actually does it. +sub img_list_fr { + Print::print_html_header_frameset( + "$Args::args{'case'}:$Args::args{'host'} Integrity Check"); + + print "<frameset rows=\"80%,20%\">\n"; + + # Block List + print "<frame src=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::IMG_LIST&" + . "$Args::baseargs\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_HASH&view=$Hash::BLANK&" + . "$Args::baseargs\" name=\"cont\">\n" + . "</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# Reads the MD5 file to fill in the MENU list for the integrity +# check mode +sub img_list { + Print::print_html_header("Image Integrity Menu"); + + my %md5s; + my @blkls; + my @str; + my @img; + my @body; + my @tl; + + # Read the known values if the file exists + if (open(FILE, "$::host_dir" . "/md5.txt")) { + + # Read the md5 values into a hash + while (<FILE>) { + s/^\s+//; + s/\s+$//; + + if (/($::REG_MD5)\s+(.*)/o) { + $md5s{"$2"} = $1; + $md5s{"$2"} =~ tr/[a-f]/[A-F]/; + } + else { + print "Error reading line $. of md5.txt: $_<br>\n"; + return 1; + } + } + close(FILE); + } + + # sort the images into the different types + foreach my $k (keys %Caseman::vol2cat) { + if ($Caseman::vol2cat{$k} eq "image") { + push @img, $k; + } + elsif ($Caseman::vol2ftype{$k} eq "blkls") { + push @blkls, $k; + } + elsif ($Caseman::vol2ftype{$k} eq "strings") { + push @str, $k; + } + elsif ($Caseman::vol2ftype{$k} eq "body") { + push @body, $k; + } + elsif ($Caseman::vol2ftype{$k} eq "timeline") { + push @tl, $k; + } + } + + print "<center><table cellspacing=\"10\" cellpadding=\"2\">"; + + # image files + if (scalar @img > 0) { + print "<tr><th colspan=3>" + . "<img src=\"pict/int_h_img.jpg\" alt=\"Image Files\">" + . "</th></tr>\n"; + my @sort = sort { $a cmp $b } @img; + int_menu_print(\%md5s, \@sort); + } + + # Unallocated (blkls) images + if (scalar @blkls > 0) { + print "<tr><th colspan=3> </th></tr>\n" + . "<tr><th colspan=3>" + . "<img src=\"pict/int_h_unalloc.jpg\" alt=\"Unallocated Data Files\">" + . "</th></tr>\n"; + my @sort = sort { $a cmp $b } @blkls; + int_menu_print(\%md5s, \@sort); + } + + # Strings files (of blkls or fs images) + if (scalar @str > 0) { + print "<tr><th colspan=3> </th></tr>\n" + . "<tr><th colspan=3>" + . "<img src=\"pict/int_h_str.jpg\" alt=\"Strings of Images\">" + . "</th></tr>\n"; + my @sort = sort { $a cmp $b } @str; + int_menu_print(\%md5s, \@sort); + + } + + # timeline body files + if (scalar @body > 0) { + print "<tr><th colspan=3> </th></tr>\n" + . "<tr><th colspan=3>" + . "<img src=\"pict/int_h_data.jpg\" alt=\"Timeline Data Files\">" + . "</th></tr>\n"; + my @sort = sort { $a cmp $b } @body; + int_menu_print(\%md5s, \@sort); + } + + # timeline files + if (scalar @tl > 0) { + print "<tr><th colspan=3> </th></tr>\n" + . "<tr><th colspan=3>" + . "<img src=\"pict/int_h_tl.jpg\" alt=\"Timelines\">" + . "</th></tr>\n"; + my @sort = sort { $a cmp $b } @tl; + int_menu_print(\%md5s, \@sort); + } + + print <<EOF; +</table> +<p> +<table cellspacing=20 width=600 cellpadding=2> +<tr> + <td><a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_CASEMAN&view=$Caseman::VOL_OPEN\" target=\"_top\"> + <img src=\"pict/menu_b_close.jpg\" alt=\"close\" + width=\"167\" height=20 border=\"0\"> + </a> + </td> + <td><a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_HASH&view=$Hash::IMG_LIST_FR\" target=\"_top\"> + <img src=\"pict/menu_b_ref.jpg\" alt=\"Refresh\" + width=\"167\" height=20 border=\"0\"> + </a> + </td> + <td align=center> + <a href=\"$::HELP_URL\" target=\"_blank\"> + <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" + width=\"167\" height=20 border=0> + </a> + </td> +</tr> +</table> + +EOF + Print::print_html_footer(); + return 0; +} + +# Pass the relative path (images/xyz) of the file. The MD5 is +# returned (or NULL) (in all caps) +sub lookup_md5 { + my $vol = shift; + my $md5 = ""; + + my $md5_file = "$::host_dir/md5.txt"; + + if (-e "$md5_file") { + unless (open(FILE, $md5_file)) { + print "Error opening $md5_file<br>\n"; + return ""; + } + + while (<FILE>) { + s/^\s+//; + s/\s+$//; + + if (/($::REG_MD5)\s+(.*)/o) { + my $m = $1; + if ($2 =~ /$vol$/) { + $md5 = $m; + $md5 =~ tr/[a-f]/[A-F]/; + last; + } + } + else { + print "Error reading line $. of $md5_file: $_<br>\n"; + return ""; + } + } + close(FILE); + } + + return $md5; +} + +sub img_verify { + + Print::print_html_header("Image Integrity Check"); + + my $vol = Args::get_vol('vol'); + + my $md5 = lookup_md5($vol); + + if ($md5 eq "") { + print +"The MD5 value of <tt>$Caseman::vol2sname{$vol}</tt> was not found<br>" + . "It can be calculated by pressing the button below." + . "<br><br>\n<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::IMG_CALC\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/int_b_calc.jpg\" " + . "alt=\"Calculate\" border=\"0\">\n</form>"; + return 1; + } + + Print::log_host_inv("$Caseman::vol2sname{$vol}: Checking image integrity"); + + print "Original MD5: <tt>$md5</tt><br>\n"; + + # We have the original value, now get the new one + my $img = $Caseman::vol2path{$vol}; + + my $cur; + if ($Caseman::vol2itype{$vol} eq "split") { + $cur = calc_md5_split($img); + } + else { + $cur = calc_md5($img); + } + + if ($cur =~ /^$::REG_MD5$/o) { + print "Current MD5: <tt>$cur</tt><br><br>\n"; + + if ($cur eq $md5) { + print "Pass<br>\n"; + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Image integrity check PASSED"); + } + else { + print "<font color=\"$::DEL_COLOR[0]\">Fail: Restore from backup" + . "<br>\n"; + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: Image integrity check FAILED"); + } + Print::print_html_footer(); + return 0; + } + else { + print "$cur<br>\n"; + Print::print_html_footer(); + return 1; + } +} + +# Calculate the MD5 value of a file (given the full path) +# return the value in upper case +# This one supports only single files - not split volumes +sub calc_md5 { + my $img = shift; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + local *OUT; + Exec::exec_pipe(*OUT, "'$::MD5_EXE' $img"); + + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + print "<br>\n" + if ($hit_cnt > 0); + + my $out = Exec::read_pipe_line(*OUT); + close(OUT); + + $out = "Error calculating MD5" + if ((!defined $out) || ($out eq "")); + + if ($out =~ /^($::REG_MD5)\s+/) { + my $m = $1; + $m =~ tr/[a-f]/[A-F]/; + return $m; + } + else { + return $out; + } +} + +# Same as the version above, but this one can do split images +# it fails though if the file is not a multiple of 512 +sub calc_md5_split { + my $img = shift; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + local *OUT; + + # We use the blkls method so that we can handle split images + Exec::exec_pipe(*OUT, "'$::TSKDIR/blkls' -f raw -e $img | '$::MD5_EXE'"); + + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + print "<br>\n" + if ($hit_cnt > 0); + + my $out = Exec::read_pipe_line(*OUT); + close(OUT); + + $out = "Error calculating MD5" + if ((!defined $out) || ($out eq "")); + + if ($out =~ /^($::REG_MD5)\s+/) { + my $m = $1; + $m =~ tr/[a-f]/[A-F]/; + return $m; + } + else { + return $out; + } +} + +# Pass it the full path and the short name +# and it adds it to md5.txt and returns the MD5 +sub int_create_wrap { + my $vol = shift; + my $img = $Caseman::vol2path{$vol}; + + my $m; + if ( (exists $Caseman::vol2itype{$vol}) + && ($Caseman::vol2itype{$vol} eq "split")) + { + $m = calc_md5_split($img); + } + else { + $m = calc_md5($img); + } + Caseman::update_md5($vol, $m) if ($m =~ /^$::REG_MD5$/o); + return $m; +} + +sub img_calc { + Print::print_html_header("Image Integrity Creation"); + my $vol = Args::get_vol('vol'); + print "Calculating MD5 value for <tt>$Caseman::vol2sname{$vol}</tt><br>\n"; + Print::log_host_inv("$Caseman::vol2sname{$vol}: Calculating MD5 value"); + + my $m = int_create_wrap($vol); + + print "MD5: <tt>$m</tt><br>\n"; + print "<br>Value saved to host file<br><br>\n"; + + Print::print_html_footer(); + return 0; +} + +# Conver the 'image' format to the 'volume' format +# Make one central file +sub convert { + my %img2vol = %{shift()}; + + Print::log_host_info("Converting format of MD5 hash files"); + + # Get out of here if there are no hash files + return 0 + unless ((-e "$::host_dir" . "$::IMGDIR" . "/md5.txt") + || (-e "$::host_dir" . "$::DATADIR" . "/md5.txt")); + + # We are going ot make a single file + my $md5_file_new = "$::host_dir" . "/md5.txt"; + open MD5_NEW, ">$md5_file_new" + or die "Can't open writing file: $md5_file_new"; + + # Read the md5s for the image directory + my $md5_file = "$::host_dir" . "$::IMGDIR" . "/md5.txt"; + if (open(FILE, $md5_file)) { + + # Read the md5 values into a hash + while (<FILE>) { + s/^\s+//; + s/\s+$//; + + if (/($::REG_MD5)\s+(.*)/o) { + my $md5 = $1; + my $img = $2; + + unless (exists $img2vol{$img}) { + print STDERR +"Error finding image during hash file conversion: $img. Skipping\n"; + next; + } + my $vol = $img2vol{$img}; + + print MD5_NEW "$md5 $vol\n"; + } + else { + print MD5_NEW "$_"; + } + } + close(FILE); + rename $md5_file, $md5_file . ".bak"; + } + + # Now do the data directory + $md5_file = "$::host_dir" . "$::DATADIR" . "/md5.txt"; + if (open(FILE, $md5_file)) { + + # Read the md5 values into a hash + while (<FILE>) { + s/^\s+//; + s/\s+$//; + + if (/($::REG_MD5)\s+(.*)/o) { + my $md5 = $1; + my $img = $2; + + unless (exists $img2vol{$img}) { + print STDERR +"Error finding image during hash file conversion: $img. Skipping\n"; + next; + } + my $vol = $img2vol{$img}; + + print MD5_NEW "$md5 $vol\n"; + } + else { + print MD5_NEW "$_"; + } + } + close(FILE); + rename $md5_file, $md5_file . ".bak"; + } + + close(MD5_NEW); + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header(""); + print "<!-- This Page Intentionally Left Blank -->\n"; + Print::print_html_footer(); + return 0; +} diff --git a/lib/Kwsrch.pm b/lib/Kwsrch.pm new file mode 100644 index 0000000000000000000000000000000000000000..a643bfa7bfa26f6a6f9be4277c2d1b135362906a --- /dev/null +++ b/lib/Kwsrch.pm @@ -0,0 +1,954 @@ +# +# Keyword search mode +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Kwsrch; + +require 'search.pl'; + +$Kwsrch::ENTER = 1; +$Kwsrch::RESULTS_FR = 2; +$Kwsrch::RUN = 3; +$Kwsrch::LOAD = 4; +$Kwsrch::BLANK = 5; + +my $IMG_DETAILS = 0x80; + +sub main { + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Kwsrch::ENTER + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view == $Kwsrch::BLANK) { + blank(); + return 0; + } + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the meta data address + if ($view == $Kwsrch::ENTER) { + return enter(); + } + elsif ($view == $Kwsrch::RESULTS_FR) { + return results_fr(); + } + elsif ($view == $Kwsrch::RUN) { + return run(); + } + elsif ($view == $Kwsrch::LOAD) { + return load(); + } + else { + Print::print_check_err("Invalid Keyword Search View"); + } +} + +my $CASE_INSENS = 1; +my $CASE_SENS = 0; + +my $REG_EXP = 1; +my $STRING = 0; + +# Form to enter search data +sub enter { + my $vol = Args::get_vol('vol'); + + Print::print_html_header("Search on $Caseman::vol2sname{$vol}"); + my $ftype = $Caseman::vol2ftype{$vol}; + + if ($ftype eq 'blkls') { + print "<center><h3>Keyword Search of Unallocated Space</h3>\n"; + } + elsif ($ftype eq 'swap') { + print "<center><h3>Keyword Search of swap partition</h3>\n"; + } + elsif ($ftype eq 'raw') { + print "<center><h3>Keyword Search of raw data</h3>\n"; + } + elsif ($Caseman::vol2cat{$vol} eq "disk") { + print "<center><h3>Keyword Search of disk</h3>\n"; + } + else { + print +"<center><h3>Keyword Search of Allocated and Unallocated Space</h3>\n"; + } + + # @@@ Fix this - caused by writing all results to a file + if ($::LIVE == 1) { + Print::print_err( +"Keyword searching is temporarily not available during live analysis mode" + ); + } + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "Enter the keyword string or expression to search for:<br> <input type=\"text\" name=\"str\"><br><br>\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n"; + + print "<table width=400><tr>\n" + . "<td width=200 align=center><input type=\"checkbox\" name=\"ascii\" value=\"1\" CHECKED>" + . "ASCII \n</td>" + . "<td width=200 align=center><input type=\"checkbox\" name=\"unicode\" value=\"1\" CHECKED>" + . "Unicode</td></tr>\n" + . "<tr><td align=center><input type=\"checkbox\" name=\"srch_case\" value=\"$CASE_INSENS\">" + . "Case Insensitive</td>\n" + . "<td align=center><input type=\"checkbox\" name=\"regexp\" value=\"$REG_EXP\">\n" + . "<tt>grep</tt> Regular Expression</td></tr></table>\n" + . "<input type=\"image\" src=\"pict/but_search.jpg\" " + . "alt=\"Search\" border=\"0\">\n</form>\n"; + + if ($::LIVE == 0) { + print "<table width=600><tr>\n"; + + # If we are a non-blkls image and one exists - make a button to load it + if (($ftype ne 'blkls') && (exists $Caseman::vol2blkls{$vol})) { + print "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_KWSRCH\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::vol2blkls{$vol}\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/srch_b_lun.jpg\" " + . "alt=\"Load Unallocated Image\" border=\"0\">\n<br></form></td>\n"; + } + + # If we are a blkls and the original exists - make a button to load it + elsif (($ftype eq 'blkls') + && (exists $Caseman::mod2vol{$vol})) + { + print "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$::MOD_KWSRCH\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$Caseman::mod2vol{$vol}\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/srch_b_lorig.jpg\" " + . "alt=\"Load Original Image\" border=\"0\">\n<br></form></td>\n"; + } + + # Strings Button + if ( (!(exists $Caseman::vol2str{$vol})) + || (!(exists $Caseman::vol2uni{$vol}))) + { + + my $dest_vol = $vol; + $dest_vol = $Caseman::mod2vol{$vol} + if exists($Caseman::mod2vol{$vol}); + + print "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_DETAILS\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$dest_vol\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/srch_b_str.jpg\" " + . "alt=\"Extract Strings\" border=\"0\">\n<br></form></td>\n"; + } + + # Unallocated Space Button + if ( ($Fs::is_fs{$ftype}) + && (!(exists $Caseman::vol2blkls{$vol}))) + { + print "<td align=center width=200>" + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_CASEMAN\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Caseman::VOL_DETAILS\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/srch_b_un.jpg\" " + . "alt=\"Extract Unallocated Space\" border=\"0\">\n<br></form></td>\n"; + } + + print "</tr></table>\n"; + } + + print "<a href=\"help/grep.html\" target=\"_blank\">" + . "Regular Expression Cheat Sheet</a>\n<br><br>\n"; + + print "<p><font color=\"red\">NOTE:</font> The keyword search runs " + . "<tt>grep</tt> on the image.<br>\n" + . "A list of what will and " + . "what will not be found is available " + . "<a href=\"help/grep_lim.html\" target=\"_blank\">here</a>.<br>\n"; + + # Section for previous searches + if ($::LIVE == 0) { + my $srch_name = get_srch_fname(0); + if (-e $srch_name) { + print "<hr><h3>Previous Searches</h3>\n" . "<table width=600>\n"; + my $row_idx = 0; + + # Cycle through the files + for (my $srch_idx = 0;; $srch_idx++) { + + $srch_name = get_srch_fname($srch_idx); + + last unless (-e $srch_name); + + # Open the file to get the string and count + unless (open(SRCH, "$srch_name")) { + print "Error opening search file: $srch_name\n"; + return 1; + } + my $prev_str = ""; + my $prev_cnt = 0; + + while (<SRCH>) { + unless (/^(\d+)\|(.*?)?\|(.*)$/) { + print + "Error pasing header of search file: $srch_name\n"; + return 1; + } + $prev_cnt = $1; + $prev_str = $3; + if (length($prev_str) > 32) { + $prev_str = substr($prev_str, 0, 32); + $prev_str .= "..."; + } + + last; + } + close(SRCH); + + print "<tr>\n" if ($row_idx == 0); + + print " <td align=center width=150>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"srchidx\" value=\"$srch_idx\">\n" + . Args::make_hidden(); + + print "<input type=\"SUBMIT\" value=\"".Print::html_encode($prev_str)." ($prev_cnt)\">" + . "<br></form>\n"; + + if ($row_idx == 3) { + print "</tr>\n"; + $row_idx = 0; + } + else { + $row_idx++; + } + } + print "</table>\n"; + } + } + + # Predefined expressions from search.pl + print "<hr><h3>Predefined Searches</h3>\n"; + print "<table width=600>\n"; + my $row_idx = 0; + my $r; + foreach $r (keys %Kwsrch::auto_srch) { + + $Kwsrch::auto_srch_reg{$r} = 0 + unless (defined $Kwsrch::auto_srch_reg{$r}); + $Kwsrch::auto_srch_csense{$r} = 1 + unless (defined $Kwsrch::auto_srch_csense{$r}); + + print "<tr>\n" if ($row_idx == 0); + + # @@@ Make a unicode option in predefined + + print " <td align=center width=150>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_KWSRCH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Kwsrch::RESULTS_FR\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"str\" value=\"$Kwsrch::auto_srch{$r}\">\n" + . "<input type=\"hidden\" name=\"ascii\" value=\"1\">\n" + . Args::make_hidden(); + + if ($Kwsrch::auto_srch_reg{$r} == 1) { + print + "<input type=\"hidden\" name=\"regexp\" value=\"$REG_EXP\">\n"; + } + if ($Kwsrch::auto_srch_csense{$r} == 0) { + print +"<input type=\"hidden\" name=\"srch_case\" value=\"$CASE_INSENS\">\n"; + } + print "<input type=\"SUBMIT\" value=\"$r\"><br></form>\n" . " </td>\n"; + + if ($row_idx == 3) { + print "</tr>\n"; + $row_idx = 0; + } + else { + $row_idx++; + } + } + print "</table>\n"; + + Print::print_html_footer(); + return 0; +} + +# MAIN WITH RESULTS +# Page that makes frame with the results on left and data units on right +sub results_fr { + my $vol = Args::get_vol('vol'); + + # A string was given for a new search + if (exists $Args::args{'str'}) { + Args::check_str(); + + Print::print_html_header_frameset( + "Search on $Caseman::vol2sname{$vol} for $Args::args{'str'}"); + + print "<frameset cols=\"35%,65%\">\n"; + + my $srch_case = ""; + $srch_case = "&srch_case=$Args::args{'srch_case'}" + if (exists $Args::args{'srch_case'}); + + my $regexp = ""; + $regexp = "®exp=$Args::args{'regexp'}" + if (exists $Args::args{'regexp'}); + + my $ascii = ""; + $ascii = "&ascii=$Args::args{'ascii'}" + if (exists $Args::args{'ascii'}); + + my $unicode = ""; + $unicode = "&unicode=$Args::args{'unicode'}" + if (exists $Args::args{'unicode'}); + + # Block List + print "<frame src=\"$::PROGNAME?" + . "mod=$::MOD_KWSRCH&view=$Kwsrch::RUN&" + . "$Args::baseargs$srch_case$regexp&str=$Args::enc_args{'str'}$ascii$unicode\">\n"; + } + elsif (exists $Args::args{'srchidx'}) { + Args::check_srchidx(); + + Print::print_html_header_frameset( +"Search on $Caseman::vol2sname{$vol} for Index $Args::args{'srchidx'}" + ); + + print "<frameset cols=\"35%,65%\">\n"; + + # Block List + print "<frame src=\"$::PROGNAME?" + . "mod=$::MOD_KWSRCH&view=$Kwsrch::LOAD&" + . "$Args::baseargs&srchidx=$Args::enc_args{'srchidx'}\">\n"; + } + + # Block Contents + print "<frame src=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::BLANK&" + . "$Args::baseargs\" name=\"content\">\n" + . "</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# Find an empty file to save the keyword searches to +sub find_srch_file { + my $vol = Args::get_vol('vol'); + + my $out_name = "$::host_dir" . "$::DATADIR/$Caseman::vol2sname{$vol}"; + my $i; + for ($i = 0; -e "${out_name}-${i}.srch"; $i++) { } + + return "${out_name}-${i}.srch"; +} + +# Pass the index +# return the full path of the file returned +sub get_srch_fname { + my $idx = shift; + my $vol = Args::get_vol('vol'); + return "$::host_dir" + . "$::DATADIR" + . "/$Caseman::vol2sname{$vol}-${idx}.srch"; +} + +sub load { + Args::check_srchidx(); + + Print::print_html_header(""); + + if ($::LIVE == 1) { + print "Searches cannot be loaded during live analysis<br>\n"; + return 1; + } + + my $srch_name = get_srch_fname($Args::args{'srchidx'}); + + print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" + . "$Args::baseargs\" " + . "target=\"_parent\">New Search</a></b>\n<p>"; + + print_srch_results($srch_name); + + Print::print_html_footer(); + return 0; +} + +# performs actual search, saves hits to file, and calls method to print +sub run { + Args::check_str(); + + Print::print_html_header(""); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $orig_str = Args::get_str(); + my $grep_str = $orig_str; # we will escape some values in the grep ver + + # Check for a search string + if ($orig_str eq "") { + print "You must enter a string value to search<br>\n"; + print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" + . "$Args::baseargs\" target=\"_parent\">New Search</a></b>\n<p>"; + return 1; + } + + my $log = ""; # Log entry string + + my $ascii = 0; + my $unicode = 0; + + if ((exists $Args::args{'ascii'}) && ($Args::args{'ascii'} == 1)) { + $ascii = 1; + $log .= "ASCII, "; + } + if ((exists $Args::args{'unicode'}) && ($Args::args{'unicode'} == 1)) { + $unicode = 1; + $log .= "Unicode, "; + } + + if (($ascii == 0) && ($unicode == 0)) { + print "You must choose either ASCII or Unicode to search<br>\n"; + print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" + . "$Args::baseargs\" target=\"_parent\">New Search</a></b>\n<p>"; + return 1; + } + + my $grep_flag = ""; # Flags to pass to grep + + # Check if search is case insensitive + my $case = 0; + if ( (exists $Args::args{'srch_case'}) + && ($Args::args{'srch_case'} == $CASE_INSENS)) + { + $grep_flag = "-i"; + $case = 1; + $log .= "Case Insensitive "; + } + + # Check if search is a regular expression + my $regexp = 0; + if ((exists $Args::args{'regexp'}) && ($Args::args{'regexp'} == $REG_EXP)) { + $grep_flag .= " -E"; + $regexp = 1; + $log .= "Regular Expression "; + } + + # if not a reg-exp, we need to escape some special values that + # 'grep' will misinterpret + else { + $grep_str =~ s/\\/\\\\/g; # \ + $grep_str =~ s/\./\\\./g; # . + $grep_str =~ s/\[/\\\[/g; # [ + $grep_str =~ s/\^/\\\^/g; # ^ + $grep_str =~ s/\$/\\\$/g; # $ + $grep_str =~ s/\*/\\\*/g; # * + # We need to add ' to end begin and end of the search as well + if ($grep_str =~ /\'/) { + $grep_str =~ s/\'/\\\'/g; # ' + $grep_str = "'$grep_str'"; + } + $grep_str =~ s/^\-/\\\-/; # starting with - (mistakes for an arg) + } + + Print::log_host_inv( + "$Caseman::vol2sname{$vol}: ${log}search for $grep_str"); + + # Get the addressable unit of image + my $bs = Args::get_unitsize(); + + # $norm_str is normalized to find the "hit" in the output + my $norm_str = $orig_str; + + # make this lowercase if we are doing case insens + $norm_str =~ tr/[A-Z]/[a-z]/ if ($case == 1); + + my $norm_str_len = length($norm_str); + + # array to pass to printing method + my @results; + + my $name_uni = ""; + my $name_asc = ""; + + # round 0 is for ASCII and 1 is for Unicode + for (my $i = 0; $i < 2; $i++) { + + next if (($i == 0) && ($ascii == 0)); + next if (($i == 1) && ($unicode == 0)); + + @results = (); + + local *OUT; + + my $hit_cnt = 0; + $SIG{ALRM} = sub { + if (($hit_cnt++ % 5) == 0) { + print "+"; + } + else { + print "-"; + } + alarm(5); + }; + + alarm(5); + + if ($i == 0) { + print "<b>Searching for ASCII</b>: "; + } + else { + print "<b>Searching for Unicode</b>: "; + } + + # if the string is less than 4 chars, then it will not be in the + # strings file so it will be searched for the slow way + if (length($orig_str) < 4) { + my $ltmp = length($orig_str); + + if ($i == 0) { + if ( (($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + } + + else { + if ( (($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d -e l -$ltmp | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::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'" + ); + } + } + } + + # Use the strings file if it exists + elsif (($i == 0) && (defined $Caseman::vol2str{$vol})) { + my $str_vol = $Caseman::vol2path{$Caseman::vol2str{$vol}}; + Exec::exec_pipe(*OUT, + "'$::GREP_EXE' $grep_flag '$grep_str' $str_vol"); + } + elsif (($i == 1) && (defined $Caseman::vol2uni{$vol})) { + my $str_vol = $Caseman::vol2path{$Caseman::vol2uni{$vol}}; + Exec::exec_pipe(*OUT, + "'$::GREP_EXE' $grep_flag '$grep_str' $str_vol"); + } + + # Run strings on the image first and then grep that + else { + if ($i == 0) { + if ( (($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + } + else { + if ( (($ftype eq "raw") || ($ftype eq "swap")) + && ($Caseman::vol2end{$vol} != 0)) + { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/blkls' -e -f $ftype -i $imgtype $img " + . $Caseman::vol2start{$vol} . "-" + . $Caseman::vol2end{$vol} + . " | '$::TSKDIR/srch_strings' -a -t d -e l | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/blkls' -e -f $ftype -o $offset -i $imgtype $img | '$::TSKDIR/srch_strings' -a -t d -e l | '$::GREP_EXE' $grep_flag '$grep_str'" + ); + } + } + } + + alarm(0); + $SIG{ALRM} = 'DEFAULT'; + + # Cycle through the results and put them in an array + while ($_ = Exec::read_pipe_line(*OUT)) { + + # Parse out the byte offset and hit string + if (/^\s*(\d+)\s+(.+)$/) { + my $off = $1; + my $hit_str_orig = $2; + my $idx = 0; + + # Make a copy that we can modify & play with + my $hit_str = $hit_str_orig; + $hit_str =~ tr/[A-Z]/[a-z]/ if ($case == 1); + + # How long was the string that we hit? + my $hit_str_len = length($hit_str); + + # I'm not sure how to find a grep re in the hit yet, so + # for now we do not get the exact offset + if ($regexp) { + my $b = int($off / $bs); + my $o = int($off % $bs); + + # $hit =~ s/\n//g; + push @results, "${b}|${o}|"; + next; + } + + # There could be more than one keyword in the string + # so cycle through all of them + my $psize = scalar(@results); + while (($idx = index($hit_str, $norm_str, $idx)) > -1) { + + # The summary of the hit starts 5 chars before it + my $sum_min = $idx - 5; + $sum_min = 0 if ($sum_min < 0); + + # Goto 5 after, if there is still space + my $sum_max = $idx + $norm_str_len + 5; + $sum_max = $hit_str_len if ($sum_max > $hit_str_len); + + my $sum_hit = + substr($hit_str_orig, $sum_min, $sum_max - $sum_min); + + # remove new lines + $sum_hit =~ s/\n/ /g; + + # The actual offset for Unicode is 2 bytes per char + my $tmpidx = $idx; + $tmpidx *= 2 + if ($i == 1); + + my $b = int(($off + $tmpidx) / $bs); + my $o = int(($off + $tmpidx) % $bs); + + push @results, "${b}|${o}|$sum_hit"; + + # advance index to find next hit + $idx++; + } + + # If we did not find a term, then just print what + # was found-this occurs bc index does not find it + # sometimes. + if ($psize == scalar(@results)) { + + my $b = int($off / $bs); + my $o = int($off % $bs); + + # $hit =~ s/\n//g; + push @results, "${b}|${o}|"; + next; + } + } + + # A negative offset is common on FreeBSD with large images + elsif (/^\s*(\-\d+):?\s*(.+)$/) { + print "ERROR: Negative byte offset ($1) Your version of " + . "strings likely does not support large files: $2<br>\n"; + } + else { + print "Error parsing grep result: $_<br>\n"; + } + } + close(OUT); + + print " <b>Done</b><br>"; + my $cnt = scalar(@results); + + my $srch_name = ""; + if ($::LIVE == 0) { + print "<b>Saving</b>: "; + + # Find a file to save the results to + $srch_name = find_srch_file(); + unless (open(IDX, ">$srch_name")) { + print "Error opening $srch_name\n"; + return (1); + } + + # Print the header + if ($i == 0) { + print IDX "$cnt|${grep_flag}|${orig_str}|ascii\n"; + $name_asc = $srch_name; + } + else { + print IDX "$cnt|${grep_flag}|${orig_str}|unicode\n"; + $name_uni = $srch_name; + } + + for (my $a = 0; $a < $cnt; $a++) { + print IDX "$results[$a]\n"; + } + close(IDX); + print " <b>Done</b><br>\n"; + } + if ($i == 0) { + print "$cnt hits"; + print "- <a href=\"#ascii\">link to results</a>" if ($cnt > 0); + print "<br>\n"; + } + else { + print "$cnt hits"; + print "- <a href=\"#unicode\">link to results</a>" if ($cnt > 0); + print "<br>\n"; + } + print "<hr>\n"; + } + + print "<b><a href=\"$::PROGNAME?mod=$::MOD_KWSRCH&view=$Kwsrch::ENTER&" + . "$Args::baseargs\" " + . "target=\"_parent\">New Search</a></b>\n<p>"; + + if ($::LIVE == 0) { + if ($ascii == 1) { + print_srch_results($name_asc); + } + + if ($unicode == 1) { + print_srch_results($name_uni); + } + } + + Print::print_html_footer(); + return 0; +} + +# Args are search string, grep flags, and array of hits +sub print_srch_results { + + if (scalar(@_) != 1) { + print "Missing Args for print_srch_results()\n"; + return 1; + } + + my $srch_name = shift; + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + + my $addr_str = $Fs::addr_unit{$ftype}; + + unless (open(SRCH, "$srch_name")) { + print "Error opening search file: $srch_name\n"; + return 1; + } + + my @results; + my $grep_str = ""; + my $grep_flag = ""; + my $cnt = 0; + my $type = 0; # ASCII iis 0 and Unicode is 1 + + my $prev = -1; + + while (<SRCH>) { + + # The first line is a header + if ($. == 1) { + if (/^(\d+)\|(.*?)?\|(.*)$/) { + $cnt = $1; + $grep_flag = $2; + $grep_str = $3; + $type = 0; + } + else { + print "Error pasing header of search file: $srch_name\n"; + close(SRCH); + return 1; + } + + if ($grep_str =~ /^(.*?)\|unicode$/) { + $grep_str = $1; + $type = 1; + } + elsif ($grep_str =~ /^(.*?)\|ascii$/) { + $grep_str = $1; + } + + my $grep_str_html = Print::html_encode($grep_str); + print "<hr>\n"; + if ($type == 0) { + print "<a name=\"ascii\">\n"; + } + else { + print "<a name=\"unicode\">\n"; + } + + if ($cnt == 0) { + print "<b><tt>$grep_str_html</tt> was not found</b><br>\n"; + } + elsif ($cnt == 1) { + print +"<b>1 occurrence of <tt>$grep_str_html</tt> was found</b><br>\n"; + } + else { + print +"<b>$cnt occurrences of <tt>$grep_str_html</tt> were found</b><br>\n"; + } + + print "Search Options:<br>\n"; + if ($type == 0) { + print " ASCII<br>\n"; + } + else { + print " Unicode<br>\n"; + } + + if ($grep_flag =~ /\-i/) { + print " Case Insensitive<br>\n"; + } + else { + print " Case Sensitive<br>\n"; + } + if ($grep_flag =~ /\-E/) { + print " Regular Expression<br>\n"; + } + + print "<hr>\n"; + + if ($cnt > 1000) { + print "There were more than <U>1000</U> hits.<br>\n"; + print "Please revise the search to a managable amount.\n"; + print + "<p>The $cnt hits can be found in: <tt>$srch_name</tt><br>\n"; + close(SRCH); + return 0; + } + + next; + } + + unless (/^(\d+)\|(\d+)\|(.*)?$/) { + print "Error parsing results: $_\n"; + close(SRCH); + return 1; + } + + my $blk = $1; + my $off = $2; + my $str = $3; + + if ($blk != $prev) { + my $url = +"$::PROGNAME?mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&$Args::baseargs&block=$blk"; + + print "<br>\n$addr_str $blk (<a href=\"$url&sort=$Data::SORT_HEX\" " + . "target=content>Hex</a> - " + . "<a href=\"$url&sort=$Data::SORT_ASC\" target=content>" + . "Ascii</a>"; + + print +" - <a href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_DATA&view=$Data::CONT_MENU_FR&" + . "mnt=$Args::enc_args{'mnt'}&vol=$Caseman::mod2vol{$vol}&" + . "btype=$Data::ADDR_BLKLS&block=$blk\" target=content>Original</a>" + if ( ($ftype eq 'blkls') + && (exists $Caseman::mod2vol{$vol})); + + print ")<br>"; + $prev = $blk; + } + + my $occ = $. - 1; + if ($str ne "") { + $str = Print::html_encode($str); + print "$occ: $off (<tt>$str</tt>)<br>\n"; + } + else { + print "$occ: $off<br>\n"; + } + } + + close(SRCH); + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header(""); + print "<!-- This Page Intentionally Left Blank -->\n"; + Print::print_html_footer(); + return 0; +} + +1; diff --git a/lib/Main.pm b/lib/Main.pm new file mode 100644 index 0000000000000000000000000000000000000000..88c39f67a72a814b2936c82006889baa58371575 --- /dev/null +++ b/lib/Main.pm @@ -0,0 +1,357 @@ +# +# Main.pm +# Autopsy Forensic Browser +# +# This file requires The Sleuth Kit +# www.sleuthkit.org +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +use lib './'; +use strict; + +use Appsort; +use Appview; +use Args; +use Caseman; +use Data; +use Exec; +use File; +use Filesystem; +use Frame; +use Fs; +use Hash; +use Kwsrch; +use Meta; +use Notes; +use Print; +use Timeline; +use Vs; + +require 'conf.pl'; +require 'define.pl'; + +# Get rid of insecure settings +$ENV{PATH} = ""; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# Formats for regular expressions +# Year.Mon.Day Hr:Min:Sec (TZ) +$::REG_DAY = '\d\d\d\d\-\d\d\-\d\d'; +$::REG_TIME = '\d\d:\d\d:\d\d'; +$::REG_ZONE_ARGS = '[\w\+\-\/\_]+'; +$::REG_ZONE2 = '\([\w\+\- ]*\)'; +$::REG_DATE = "$::REG_DAY" . '\s+' . "$::REG_TIME" . '\s+' . "$::REG_ZONE2"; +$::REG_FTYPE = '[\w\-]+'; +$::REG_SKEW = '[\+\-]?\d+'; +$::REG_MTYPE = '[\?bcdflprsvw-]'; # Type according to meta data + +$::REG_FILE = '[\w\-\_\.]+'; +$::REG_CASE = $::REG_FILE; +$::REG_HOST = '[\w\-\_\.]+'; +$::REG_INVESTIG = '[\w]+'; + +$::REG_IMG = "$::REG_FILE" . '/' . "$::REG_FILE"; +$::REG_IMG_PATH = '/[\w\-\_\.\/]+'; +$::REG_IMG_PATH_WILD = '/[\w\-\_\.\/]+\*?'; +$::REG_IMG_CONFIG = '[\w\-\_\.\/ ]+'; +$::REG_FNAME = $::REG_FILE; +$::REG_MNT = '[\w\-\_\.\/\:\\\\]+'; +$::REG_SEQ_FILE = '[\w\s\-\_\.\/\:\\\\]+'; +$::REG_HASHDB = '[\w\-\_\.\,\/]+'; +$::REG_IMGTYPE = '[\w\,]+'; +$::REG_INAME = '[\w]+'; +$::REG_VNAME = '[\w]+'; + +$::REG_META = '[\d-]+'; +$::REG_MD5 = '[0-9a-fA-F]{32,32}'; + +$::HELP_URL = "help/index.html"; + +# host_dir and case_dir will end with a '/' +$::host_dir = ""; +$::case_dir = ""; + +################## NEW STUFF ########################## +# MODULES + +# If the values of these are changed, or if new modules are added, +# Then the below pseudo-binary sort algorithm must be changed as well +$::MOD_CASEMAN = 0; +$::MOD_FRAME = 1; +$::MOD_FILE = 2; +$::MOD_META = 3; +$::MOD_KWSRCH = 4; +$::MOD_DATA = 5; +$::MOD_TL = 6; +$::MOD_FS = 7; +$::MOD_APPSORT = 8; +$::MOD_NOTES = 9; +$::MOD_HASH = 10; +$::MOD_APPVIEW = 11; + +# Main Menu +# +# Display the title page +sub welcome { + Print::print_html_header_javascript("Autopsy Forensic Browser"); + + print "<center>\n"; + + # This problem has not been seen with the 1 second delay + if ((0) && ($] >= 5.008)) { + print +"<p><font color=\"red\">Warning: You are using Perl v5.8.</font><br>\n" + . " Some buffer problems have been reported with Autopsy and Perl 5.8 " + . "where output is not shown.<br>\n" + . "Perl 5.6 should be used if available.\n" + . "If data is missing, reload the page<br><hr>\n"; + } + + print <<EOF; +<table cellspacing=0 cellpadding=2 width=600 height=350 border=0> + +<tr> + <td colspan=\"3\" align=\"center\" valign=\"MIDDLE\"> + <b>Autopsy Forensic Browser $::VER</b> + </td> +</tr> +<tr> + <td colspan=\"3\"> </td> +</tr> +<tr> + <td colspan=\"3\" align=\"center\" valign=\"MIDDLE\"> + <a href=\"./about\"> + <img src=\"pict/logo.jpg\" border=0 alt="Logo"> + </a> + </td> +</tr> +<tr> + <td colspan=\"3\"> </td> +</tr> +<tr> + <td colspan=\"3\" align=\"center\" valign=\"MIDDLE\"> + <a href="http://www.sleuthkit.org/autopsy/"> + <tt>http://www.sleuthkit.org/autopsy/</tt> + </a> + </td> +</tr> +<tr><td colspan=3> </td></tr> +<tr> + <td align=center width=200 valign=\"MIDDLE\"> + <a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::CASE_OPEN\"> + <img src=\"pict/menu_b_copen.jpg\" alt=\"Open Case\" width=176 height=20 border=0> + </a> + </td> + <td align=center width=200 valign=\"MIDDLE\"> + <a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::CASE_NEW\"> + <img src=\"pict/menu_b_cnew.jpg\" alt=\"New Case\" width=176 height=20 border=0> + </a> + </td> + <td align=center width=200 valign=\"MIDDLE\"> + <a href=\"$::HELP_URL\" target=\"_blank\"> + <img src=\"pict/menu_b_help.jpg\" alt=\"Help\" width=167 height=20 border=0> + </a> + </td> +</tr> +</table> + +EOF + + Print::print_html_footer_javascript(); + return 0; +} + +sub get_tskver { + local *OUT; + Exec::exec_pipe(*OUT, "'$::TSKDIR/fls' -V"); + + my $ver = Exec::read_pipe_line(*OUT); + $ver = $1 if ($ver =~ /^The Sleuth Kit ver (.*)$/); + close(OUT); + + return $ver; +} + +# This function is called by the code in the 'autopy' file. +# This will check for the basic module arguments and then host +# and case accordingly. The main function of each of the modules +# is called from here. Each of the modules will have to take care +# of the detailed argument checking. + +sub main { + + # Parse arguments + my $lcl_args = shift; + Args::parse_args($lcl_args); + + # When autopsy is first run, no mod or arguments are given. + unless (exists $Args::args{'mod'}) { + + # if we are not in live analysis mode, display the usual screen + if ($::LIVE == 0) { + return welcome(); + } + else { + + # If we are in live analysis mode, open up the window to select + # and image and supply basic host and case values. + $Args::args{'mod'} = $Args::enc_args{'mod'} = $::MOD_CASEMAN; + $Args::args{'view'} = $Args::enc_args{'view'} = $Caseman::VOL_OPEN; + $Args::args{'case'} = $Args::enc_args{'case'} = "live"; + $Args::args{'host'} = $Args::enc_args{'host'} = "local"; + $Args::args{'inv'} = $Args::enc_args{'inv'} = "unknown"; + } + } + + Args::check_mod(); + my $module = Args::get_mod(); + + Args::make_baseargs(); + + # For live analysis, we need to change the regular expression + # for images because it can be a full path (to /dev/xxxxxxx) + $::REG_IMG = '/[\w\-\_\.\/]+' + if ($::LIVE == 1); + + # The Case Management module is handled seperately because + # it may not have the host and case values + if ($module == $::MOD_CASEMAN) { + return Caseman::main(); + } + + # Check the minimum arguments + Args::check_case(); + Args::check_host(); + + # Set the case and host variables + if ($::LIVE == 0) { + $::case_dir = "$::LOCKDIR/" . Args::get_case() . "/"; + $::case_dir =~ s/\/\//\//g; + $::host_dir = "$::case_dir" . Args::get_host() . "/"; + $::host_dir =~ s/\/\//\//g; + } + else { + $::host_dir = ""; + $::case_dir = ""; + } + Caseman::read_host_config(); + + # This is a partial binary sort method to reduce the number of checks + + # 0 < mod < 6 + if ($module < $::MOD_TL) { + + # 0 < mod < 4 + if ($module < $::MOD_KWSRCH) { + + # mod == 1 + if ($module == $::MOD_FRAME) { + return Frame::main(); + } + + # mod == 2 + elsif ($module == $::MOD_FILE) { + return File::main(); + } + + # mod == 3 + elsif ($module == $::MOD_META) { + return Meta::main(); + } + } + + # 4 <= mod < 6 + else { + + # mod == 4 + if ($module == $::MOD_KWSRCH) { + return Kwsrch::main(); + } + + # mod == 5 + elsif ($module == $::MOD_DATA) { + return Data::main(); + } + } + } + + # 6 <= mod + else { + + # 6 <= mod < 9 + if ($module < $::MOD_NOTES) { + + # mod == 6 + if ($module == $::MOD_TL) { + return Timeline::main(); + } + + # mod == 7 + elsif ($module == $::MOD_FS) { + return Filesystem::main(); + } + + # mod == 8 + elsif ($module == $::MOD_APPSORT) { + return Appsort::main(); + } + } + + # 9 <= mod + else { + + # mod == 9 + if ($module == $::MOD_NOTES) { + return Notes::main(); + } + + # mod == 10 + elsif ($module == $::MOD_HASH) { + return Hash::main(); + } + + # mod == 11 + elsif ($module == $::MOD_APPVIEW) { + return Appview::main(); + } + + # New modules can be added here + + } + } + Print::print_check_err("Invalid Module"); +} + +1; diff --git a/lib/Meta.pm b/lib/Meta.pm new file mode 100644 index 0000000000000000000000000000000000000000..a0728c06b935707fa341118c94222fbf1a8bf851 --- /dev/null +++ b/lib/Meta.pm @@ -0,0 +1,789 @@ +# +# Metadata mode +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Updated 1/13 + +package Meta; + +$Meta::FRAME = 0; +$Meta::ENTER = 1; +$Meta::STATS = 2; +$Meta::EXPORT = 3; +$Meta::FFIND = 4; +$Meta::LIST = 5; +$Meta::REPORT = 6; +$Meta::BLANK = 7; + +sub main { + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Meta::FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + # Check Basic Args + Args::check_vol('vol'); + + # These windows don't need the meta data address + if ($view == $Meta::FRAME) { + return frame(); + } + elsif ($view == $Meta::ENTER) { + return enter(); + } + elsif ($view == $Meta::LIST) { + return list(); + } + elsif ($view == $Meta::BLANK) { + return blank(); + } + + # These windows do need the meta data address + Args::check_meta('meta'); + if ($view == $Meta::STATS) { + return stats(); + } + elsif ($view == $Meta::FFIND) { + return findfile(); + } + + Args::check_recmode(); + if ($view == $Meta::EXPORT) { + return export(); + } + elsif ($view == $Meta::REPORT) { + return report(); + } + else { + Print::print_check_err("Invalid Meta View"); + } + +} + +# Print the two frames +sub frame { + + my $vol = Args::get_vol('vol'); + + Print::print_html_header_frameset( + "Meta Data Browse on $Caseman::vol2sname{$vol}"); + print "<frameset cols=\"20%,80%\">\n"; + + # Print the frame where an addres can be entered and a frame for the + # contents + if (exists $Args::enc_args{'meta'}) { + print +"<frame src=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::ENTER&$Args::baseargs" + . "&meta=$Args::enc_args{'meta'}\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::STATS&" + . "meta=$Args::enc_args{'meta'}&$Args::baseargs\" " + . "name=\"content\">\n</frameset>\n"; + } + else { + print +"<frame src=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::ENTER&$Args::baseargs\">\n" + . "<frame src=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::BLANK&$Args::baseargs\" " + . "name=\"content\">\n</frameset>\n"; + } + + Print::print_html_footer_frameset(); + return 0; +} + +# Generate the frame to enter the data into +sub enter { + Print::print_html_header(""); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + + # Address + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"content\">\n" + . "<b>$Fs::meta_str{$ftype} Number:</b><br>   " + . "<input type=\"text\" name=\"meta\" size=12 maxlength=12"; + + print " value=\"$Args::enc_args{'meta'}\"" if exists($Args::args{'meta'}); + + print ">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_META\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Meta::STATS\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . Args::make_hidden() + . + + # View Button + "<p><input type=\"image\" src=\"pict/but_view.jpg\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></form>\n"; + + # Allocation List + print "<hr><p>" + . "<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::LIST&$Args::baseargs\" target=\"content\">" + . "<img src=\"pict/but_alloc_list.jpg\" border=\"0\" " + . "width=113 height=20 alt=\"Allocation List\">" + . "</a>\n"; + + Print::print_html_footer(); + return 0; +} + +# Display the contents of meta +sub stats { + Print::print_html_header(""); + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Displaying details of $Fs::meta_str{$ftype} $meta" + ); + + my $meta_int = $meta; + $meta_int = $1 if ($meta =~ /^(\d+)-\d+(-\d)?/); + + my $prev = $meta_int - 1; + my $next = $meta_int + 1; + + # We need to get the allocation status of this structure + my $recmode = $File::REC_NO; + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ils' -f $ftype -e -o $offset -i $imgtype $img $meta_int"); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + next unless ($_ =~ /^$meta_int/); + if ($_ =~ /^$meta_int\|f/) { + $recmode = $File::REC_YES; + } + elsif ($_ =~ /^$meta_int\|a/) { + $recmode = $File::REC_NO; + } + else { + Print::print_err("Error parsing ils output: $_"); + } + } + close(OUT); + + print "<center>\n"; + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::STATS&$Args::baseargs&meta=$prev\">" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a>\n" + unless ($prev < $Fs::first_meta{$ftype}); + + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::STATS&$Args::baseargs&meta=$next\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a>\n<br>"; + + # Report + print "<table cellspacing=\"0\" cellpadding=\"2\">\n<tr>" + . "<td><a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::REPORT" + . "&$Args::baseargs&meta=$meta&recmode=$recmode\"" + . " target=\"_blank\">" + . "<img src=\"pict/but_report.jpg\" alt=\"report\" " + . "width=88 height=20 border=\"0\">" + . "</a></td>\n"; + + # View (File Mode) + print "<td><a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR" + . "&$Args::baseargs&meta=$meta&recmode=$recmode" + . "&dir=$vol-meta-$meta\" target=\"_blank\">" + . "<img src=\"pict/but_viewcont.jpg\" alt=\"view contents\" " + . "width=123 height=20 border=\"0\">" + . "</a></td>\n"; + + # Export + print "<td><a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::EXPORT" + . "&$Args::baseargs&meta=$meta&recmode=$recmode\">" + . "<img src=\"pict/but_export.jpg\" alt=\"export\" " + . "width=123 height=20 border=\"0\">" + . "</a></td>"; + + # Notes + print "<td><a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::ENTER_FILE" + . "&$Args::baseargs&meta=$meta&\" " + . "target=\"_blank\">" + . "<img src=\"pict/but_addnote.jpg\" alt=\"Add Note\" " + . "width=\"89\" height=20 border=\"0\">" + . "</a></td>" + if ($::USE_NOTES == 1); + + print "</tr></table>\n</center>\n"; + + my $tmpr = $Caseman::vol2mnt{$vol}; + + if ($ftype =~ /fat/) { + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::FFIND&$Args::baseargs&" + . "meta=$meta\" target=\"_blank\">Search for File Name</a><br><br>"; + } + else { + + print "<b>Pointed to by file:</b><br>\n"; + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ffind' -f $ftype -a -o $offset -i $imgtype $img $meta"); + my $cnt = 0; + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + if (/^(\*)\s+\/*(.*)$/) { + Print::print_output("<tt><font color=\"$::DEL_COLOR[0]\">" + . Print::html_encode($tmpr . $2) + . "</font></tt> (deleted)<br><br>\n"); + } + elsif (/^\/(.*)$/) { + Print::print_output("<tt>" + . Print::html_encode($tmpr . $1) + . "</tt><br><br>\n"); + } + else { + Print::print_output(Print::html_encode($_) . "<br><br>\n"); + } + $cnt++; + } + close(OUT); + if ($cnt == 0) { + print "<br>Invalid $Fs::meta_str{$ftype} value<br><br>\n"; + return; + } + } + + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + my $file_type = Exec::read_pipe_line(*OUT); + close(OUT); + + $file_type = "Error getting file type" + if ((!defined $file_type) || ($file_type eq "")); + + if ($recmode == $File::REC_YES) { + print "<b>File Type (Recovered):</b><br>$file_type<br>\n"; + } + else { + print "<b>File Type:</b><br>$file_type<br><br>\n"; + } + + # MD5 Value + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::MD5_EXE'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::MD5_EXE'" + ); + } + + my $md5out = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5out = "Error getting MD5" + if ((!defined $md5out) || ($md5out eq "")); + + chomp $md5out; + if ($recmode == $File::REC_YES) { + print "<b>MD5 of recovered content:</b><br><tt>$md5out</tt><br><br>\n"; + } + else { + print "<b>MD5 of content:</b><br><tt>$md5out</tt><br><br>\n"; + } + + # Hash Database Lookups + if ( + ( + ($::NSRLDB ne "") + || ($Caseman::alert_db ne "") + || ($Caseman::exclude_db ne "") + ) + && ($md5out =~ /^$::REG_MD5$/o) + ) + { + + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_blank\">\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"md5\" value=\"$md5out\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_HASH\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Hash::DB_LOOKUP\">\n" + . "<table cellpadding=\"2\" cellspacing=\"8\"><tr>\n"; + + if ($::NSRLDB ne "") { + print "<td align=\"left\">" + . "<input type=\"checkbox\" name=\"hash_nsrl\" value=\"1\" CHECKED>" + . "NSRL</td>\n"; + } + if ($Caseman::alert_db ne "") { + print "<td align=\"left\">" + . "<input type=\"checkbox\" name=\"hash_alert\" value=\"1\" CHECKED>" + . "Alert Database</td>\n"; + } + if ($Caseman::exclude_db ne "") { + print "<td align=\"left\">" + . "<input type=\"checkbox\" name=\"hash_exclude\" value=\"1\" CHECKED>" + . "Exclude Database</td>\n"; + } + print "<td align=\"left\">" + . "<input type=\"image\" src=\"pict/but_lookup.jpg\" " + . "width=116 height=20 alt=\"Ok\" border=\"0\">" + . "</td></tr></table>\n</form>\n"; + } + + # SHA-1 Value + if ($::SHA1_EXE ne "") { + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::SHA1_EXE'" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::SHA1_EXE'" + ); + } + + my $sha1out = Exec::read_pipe_line(*OUT); + close(OUT); + + $sha1out = "Error getting SHA-1" + if ((!defined $sha1out) || ($sha1out eq "")); + + chomp $sha1out; + if ($recmode == $File::REC_YES) { + print +"<b>SHA-1 of recovered content:</b><br><tt>$sha1out</tt><br><br>\n"; + } + else { + print "<b>SHA-1 of content:</b><br><tt>$sha1out</tt><br><br>\n"; + } + } + + # istat output + print "<b>Details:</b><br><br>\n"; + my $mode = 0; # set to 1 when showing blocks + my $force = 0; # set to 1 if size of meta is 0 + + my @output; + if (exists($Args::args{'force'})) { + my $f = Args::get_force(); + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -B $f -o $offset -i $imgtype $img $meta" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + } + while ($_ = Exec::read_pipe_line(*OUT)) { + if ($mode == 1) { + if (/^Indirect Blocks/) { + print "$_<br>\n"; + next; + } + elsif (/^Recover/) { + print "$_<br>\n"; + next; + } + elsif (/^Type: (\S+) \((\d+\-\d+)\) (.*)$/) { + print "$1 (" + . "<a href=\"$::PROGNAME?mod=$::MOD_FILE&view=$File::CONT_FR&$Args::baseargs" + . "&meta=$meta_int-$2&dir=$vol-meta-$meta_int-$2\" " + . "target=\"_blank\">$2</a>) $3<br>\n"; + next; + } + + my $blk; + foreach $blk (split(/ /, $_)) { + if ($blk =~ /^\d+$/) { + print +"<a href=\"$::PROGNAME?mod=$::MOD_FRAME&submod=$::MOD_DATA&" + . "$Args::baseargs&block=$blk\" target=\"_top\">$blk</a> "; + } + else { + print "$blk "; + } + } + print "<br>\n"; + } + else { + if (/^Not Allocated$/) { + print "<font color=\"$::DEL_COLOR[0]\">$_</font><br>\n"; + } + else { + print "$_<br>\n"; + } + $mode = 1 if (/^Direct Blocks|^Sectors/); + $mode = 1 if (/^Attributes:/); # HFS gets messed up without ":" + $mode = 1 if (/^Data Fork Blocks:/); + + if ((/^size: (\d+)/) && ($1 == 0)) { + $force = 1; + } + } + } + close(OUT); + + # display a text box to force X number of blocks to be displayed + if ($force == 1) { + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . Args::make_hidden() + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"meta\" value=\"$meta\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_META\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Meta::STATS\">\n"; + + print +"Enter number of $Fs::addr_unit{$ftype}s to display: <input type=\"text\" " + . "value=5 name=\"force\" size=\"3\">\n"; + + print "<input type=\"image\" src=\"pict/but_force.jpg\" " + . "width=53 height=20 alt=\"Force\" border=\"0\"> (because the size is 0)\n</form>\n"; + } + + Print::print_html_footer(); + return 0; +} + +sub findfile { + + Print::print_html_header("Find File"); + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $tmpr = $Caseman::vol2mnt{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + print "<b>Pointed to by file:</b><br>\n"; + + local *OUT; + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ffind' -f $ftype -a -o $offset -i $imgtype $img $meta"); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + if (/(\*)\s+\/*(.*)/) { + Print::print_output("<tt><font color=\"$::DEL_COLOR[0]\">" + . Print::html_encode($tmpr . $2) + . "</font></tt> (deleted)<br>\n"); + } + elsif (/^\/(.*)$/) { + Print::print_output( + "<tt>" . Print::html_encode($tmpr . $1) . "</tt><br>\n"); + } + else { + Print::print_output(Print::html_encode($_) . "<br>\n"); + } + } + close(OUT); + + Print::print_html_footer(); + return 0; +} + +sub export { + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $recmode = Args::get_recmode(); + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Saving contents of $Fs::meta_str{$ftype} $meta" + ); + + Print::print_oct_header("$vol" . "-meta" . "$meta" . ".raw"); + + local *OUT; + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta"); + } + else { + Exec::exec_pipe(*OUT, + "'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta"); + } + + print "$_" while ($_ = Exec::read_pipe_data(*OUT, 512)); + close(OUT); + + Print::print_oct_footer(); +} + +sub report { + + my $meta = Args::get_meta('meta'); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + my $recmode = Args::get_recmode(); + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: Generating report for $Fs::meta_str{$ftype} $meta" + ); + + Print::print_text_header("filename=$vol-meta$meta.txt"); + + print " Autopsy $Fs::meta_str{$ftype} Report\n\n" + . "-" x 70 . "\n" + . " GENERAL INFORMATION\n\n" + . "$Fs::meta_str{$ftype}: $Args::args{'meta'}\n"; + + print "Pointed to by file(s):\n"; + my $tmpr = $Caseman::vol2mnt{$vol}; + local *OUT; + + Exec::exec_pipe(*OUT, + "'$::TSKDIR/ffind' -f $ftype -a -o $offset -i $imgtype $img $meta"); + while ($_ = Exec::read_pipe_line(*OUT)) { + chop; + if (/^(\*)\s+\/*(.*)$/) { + Print::print_output( + " ${tmpr}${2} (deleted)\n"); + } + elsif (/^\/(.*)$/) { + Print::print_output(" ${tmpr}${1}\n"); + } + else { + Print::print_output(" $_\n"); + } + } + close(OUT); + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta | '$::MD5_EXE'" + ); + my $md5 = Exec::read_pipe_line(*OUT); + close(OUT); + + $md5 = "Error getting MD5 Value" + if ((!defined $md5) || ($md5 eq "")); + + chop $md5; + print "MD5 of istat output: $md5\n"; + + if ($::SHA1_EXE ne "") { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta | '$::SHA1_EXE'" + ); + my $sha1 = Exec::read_pipe_line(*OUT); + close(OUT); + + $sha1 = "Error getting SHA-1 Value" + if ((!defined $sha1) || ($sha1 eq "")); + + chop $sha1; + print "SHA-1 of istat output: $sha1\n"; + } + + print "\nImage: $Caseman::vol2path{$vol}\n"; + if (($Caseman::vol2start{$vol} == 0) && ($Caseman::vol2end{$vol} == 0)) { + print "Offset: Full image\n"; + } + elsif ($Caseman::vol2end{$vol} == 0) { + print "Offset: $Caseman::vol2start{$vol} to end\n"; + } + else { + print "Offset: $Caseman::vol2start{$vol} to $Caseman::vol2end{$vol}\n"; + } + print "File System Type: $ftype\n"; + + my $date = localtime(); + print "\nDate Generated: $date\n" + . "Investigator: $Args::args{'inv'}\n\n" + . "-" x 70 . "\n" + . " META DATA INFORMATION\n\n"; + + Exec::exec_pipe(*OUT, +"'$::TSKDIR/istat' -f $ftype $tz -s $Caseman::ts -o $offset -i $imgtype $img $meta" + ); + while ($_ = Exec::read_pipe_line(*OUT)) { + print $_; + } + close(OUT); + + if ($recmode == $File::REC_YES) { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -r -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + else { + Exec::exec_pipe(*OUT, +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $meta | '$::FILE_EXE' -z -b -" + ); + } + + my $file_type = Exec::read_pipe_line(*OUT); + close(OUT); + + $file_type = "Error getting file type" + if ((!defined $file_type) || ($file_type eq "")); + + print "\nFile Type: $file_type"; + + print "\n" + . "-" x 70 . "\n" + . " VERSION INFORMATION\n\n" + . "Autopsy Version: $::VER\n"; + print "The Sleuth Kit Version: " . ::get_tskver() . "\n"; + + Print::print_text_footer(); + + return 0; +} + +# Display the meta Allocation Table +sub list { + my $ILS_GAP = 500; + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $min = 0; + + $min = Args::get_min() if (exists $Args::args{'min'}); + my $max = $min + $ILS_GAP - 1; + + # Because we can not use metas 0 and 1 for most FS, set fmin to the + # minimum for this fs + my $fmin = $min; + $fmin = $Fs::first_meta{$ftype} if ($min < $Fs::first_meta{$ftype}); + + Print::print_html_header( + "$Fs::meta_str{$ftype} Allocation List $fmin -> $max"); + + Print::log_host_inv( +"$Caseman::vol2sname{$vol}: $Fs::meta_str{$ftype} Allocation List for $min to $max" + ); + + print "<center><H2>$Fs::meta_str{$ftype}: $fmin - $max</H2>"; + + # Display next and previous links + my $tmp; + if ($min > $Fs::first_meta{$ftype}) { + $tmp = $min - $ILS_GAP; + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::LIST&$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a> "; + } + $tmp = $min + $ILS_GAP; + print +" <a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::LIST&$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a><br>"; + print "</center>\n"; + + # The list + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ils' -e -s $Caseman::ts -f $ftype -o $offset -i $imgtype $img $fmin-$max" + ); + while ($_ = Exec::read_pipe_line(*OUT)) { + if (/^($::REG_META)\|([af])\|\d+\|\d+\|\d+\|\d+\|\d+\|/o) { + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::STATS&$Args::baseargs&meta=$1\">" + . "$1:</a> "; + if ($2 eq "a") { + print "allocated<br>\n"; + } + else { + print "<font color=\"$::DEL_COLOR[0]\">free</font><br>\n"; + } + } + } + close(OUT); + + # Display next and previous links + print "<center>\n"; + if ($min > $Fs::first_meta{$ftype}) { + $tmp = $min - $ILS_GAP; + print +"<a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::LIST&$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_prev.jpg\" alt=\"previous\" " + . "width=\"89\" height=20 border=\"0\"></a> "; + } + $tmp = $min + $ILS_GAP; + print +" <a href=\"$::PROGNAME?mod=$::MOD_META&view=$Meta::LIST&$Args::baseargs&min=$tmp\">" + . "<img src=\"pict/but_next.jpg\" alt=\"next\" " + . "width=\"89\" height=20 border=\"0\"></a><br>"; + print "</center>\n"; + + Print::print_html_footer(); + return 0; + +} + +# Blank Page +sub blank { + Print::print_html_header("Metadata Blank Page"); + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + + print "<center><h3>Metadata Mode</h3><br>\n" + . "Here you can view the details about any $Fs::meta_str{$ftype} in the file system.<br>\n" + . "These are the data structures that store the file details.<br>\n" + . "Enter the address in the field on the left.\n"; + Print::print_html_footer(); + return 0; +} + diff --git a/lib/Notes.pm b/lib/Notes.pm new file mode 100644 index 0000000000000000000000000000000000000000..5f36f1ad3ed2d32016fb4c8676452306b58b65c7 --- /dev/null +++ b/lib/Notes.pm @@ -0,0 +1,1110 @@ +# +# Notes and sequencer functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Notes; + +use POSIX; + +$Notes::ENTER_FILE = 1; +$Notes::ENTER_DATA = 2; +$Notes::WRITE_FILE = 3; +$Notes::WRITE_DATA = 4; +$Notes::WRITE_SEQ_MAN = 5; +$Notes::READ_NORM = 6; +$Notes::READ_SEQ = 7; + +sub main { + + # There is no default for Notes + Args::check_view(); + + Print::print_check_error("Notes option is not enabled") + if ($::USE_NOTES == 0); + + my $view = Args::get_view(); + + if ($view == $Notes::ENTER_FILE) { + return enter_file(); + } + elsif ($view == $Notes::ENTER_DATA) { + return enter_data(); + } + elsif ($view == $Notes::WRITE_FILE) { + return write_file(); + } + elsif ($view == $Notes::WRITE_DATA) { + return write_data(); + } + elsif ($view == $Notes::WRITE_SEQ_MAN) { + return write_seq_man(); + } + elsif ($view == $Notes::READ_NORM) { + return read_norm(); + } + elsif ($view == $Notes::READ_SEQ) { + return read_seq(); + } + else { + Print::print_check_err("Invalid Notes View"); + } +} + +sub investig_notes_fname { + return "$::host_dir" . "$::LOGDIR/$Args::args{'inv'}.notes"; +} + +sub investig_seq_notes_fname { + return "$::host_dir" . "$::LOGDIR/$Args::args{'inv'}.seq.notes"; +} + +# window where user can enter a normal and sequencer note for a file +# or meta data structure. +sub enter_file { + Args::check_vol('vol'); + Args::check_meta('meta'); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $mnt = $Caseman::vol2mnt{$vol}; + my $meta = Args::get_meta('meta'); + + # A file will have a 'dir' argument and a meta structure will not + if (exists $Args::args{'dir'}) { + my $fname = "$mnt$Args::args{'dir'}"; + Print::print_html_header("Notes for file $fname"); + print "<center><b>Enter a note for <tt>$fname</tt> ($meta):</b>" + . "<br><br>\n"; + } + else { + Print::print_html_header("Notes for $Fs::meta_str{$ftype} $meta"); + print "<center><b>Enter a note for $Fs::meta_str{$ftype} $meta:</b>" + . "<br><br>\n"; + } + print +"A note works like a bookmark and allows you to later find this data more easily.<br><br>\n"; + + # Setup the form + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<textarea rows=10 cols=50 wrap=\"virtual\" name=\"note\">" + . "</textarea><br>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_NOTES\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Notes::WRITE_FILE\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"meta\" value=\"$meta\">\n" + . Args::make_hidden(); + + print "<input type=\"hidden\" name=\"dir\" value=\"$Args::args{'dir'}\">\n" + if (exists $Args::args{'dir'}); + + # Option to add a normal note + print "<input type=\"checkbox\" name=\"norm_note\" value=\"1\" CHECKED>\n" + . " Add a Standard Note<br>\n"; + + # Sequencer notes - which requires the MAC times for the files + if ("$Caseman::tz" ne "") { + $ENV{TZ} = $Caseman::tz; + POSIX: tzset(); + } + + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $meta_int = $meta; + $meta_int = $1 if ($meta_int =~ /^(\d+)-\d+(-\d+)?$/); + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ils' -s $Caseman::ts -f $ftype -e -o $offset -i $imgtype $img $meta_int" + ); + + # Get the fourth line + my $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + close(OUT); + unless ((defined $tmp) + && ($tmp =~ /^$::REG_META\|\w\|\d+\|\d+\|(\d+)\|(\d+)\|(\d+)\|/o)) + { + Print::print_err("Error parsing 'ils' output"); + } + + my $mtime = $1; + my $atime = $2; + my $ctime = $3; + + $mtime = localtime($mtime); + $atime = localtime($atime); + $ctime = localtime($ctime); + + # Print the Times + print "<br><hr><b>Add a Sequencer Event:</b><br><br>\n" + . "A sequencer event will be sorted based on the time so that event reconstruction will be easier<br><br>\n" + . "<input type=\"checkbox\" name=\"mtime\" value=\"1\">" + . " M-Time (<tt>$mtime</tt>)<br>\n" + . "<input type=\"checkbox\" name=\"atime\" value=\"1\">" + . " A-Time (<tt>$atime</tt>)<br>\n" + . "<input type=\"checkbox\" name=\"ctime\" value=\"1\">" + . " C-Time (<tt>$ctime</tt>)<br><hr><br>\n"; + + # The OK Button + print "<br><input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return 0; +} + +# data unit comment +sub enter_data { + Args::check_vol('vol'); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $block = Args::get_block(); + my $len = Args::get_len(); + + Print::print_html_header("Notes for $Fs::addr_unit{$ftype} $block"); + + print + "<center><b>Enter a note for $Fs::addr_unit{$ftype} $block</b><br><br>\n" + . "A note works like a bookmark and allows you to later find this data more easily.<br><br>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<textarea rows=10 cols=50 wrap=\"virtual\" name=\"note\">" + . "</textarea><br>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_NOTES\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Notes::WRITE_DATA\">\n" + . "<input type=\"hidden\" name=\"vol\" value=\"$vol\">\n" + . "<input type=\"hidden\" name=\"block\" value=\"$block\">\n" + . "<input type=\"hidden\" name=\"len\" value=\"$len\">\n" + . "<input type=\"hidden\" name=\"norm_note\" value=\"1\">\n" + . Args::make_hidden() + . "<br><input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return 0; + +} + +# Write the note to the note file +sub write_data { + Args::check_vol('vol'); + Args::check_block(); + Args::check_len(); + Args::check_note(); + + Print::print_html_header("Write a note"); + + my $vol = Args::get_vol('vol'); + my $ftype = $Caseman::vol2ftype{$vol}; + my $img_sh = $Caseman::vol2sname{$vol}; + my $block = Args::get_block(); + my $len = Args::get_len(); + + Print::log_host_inv( + "$img_sh: Creating note for $Fs::addr_unit{$ftype} $block"); + + my $notes_file = investig_notes_fname(); + open NOTES, ">>$notes_file" or die "Can't open log: $notes_file"; + print "Note added to $notes_file:<p>\n\n"; + + # Date + my $tmp = localtime(); + print NOTES "$tmp\n"; + print "$tmp<br>\n"; + + print NOTES "Volume: $vol $Fs::addr_unit{$ftype}: $block Len: $len\n"; + print "Volume: $vol $Fs::addr_unit{$ftype}: $block Len: $len<br>\n"; + + # The actual notes and a line at the bottom + print NOTES "\n$Args::args{'note'}\n\n" . "-" x 70 . "\n"; + print "<p>".Print::html_encode($Args::args{'note'})."<p>"; + close(NOTES); + + print "<hr>\n" + . "You can view the notes from the Host Manager View<p>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_NORM\">" + . "<img border=0 src=\"pict/menu_b_note.jpg\" " + . "width=\"167\" height=20 alt=\"View Notes\"></a>\n"; + + Print::print_html_footer(); + + return 0; +} + +sub write_file { + Args::check_vol('vol'); + Args::check_meta('meta'); + Args::check_note(); + + # Get rid of carriage returns that Netscape adds + $Args::args{'note'} =~ tr/\r//d; + + my $vol = Args::get_vol('vol'); + my $mnt = $Caseman::vol2mnt{$vol}; + + my $ftype = $Caseman::vol2ftype{$vol}; + my $img_sh = $Caseman::vol2sname{$vol}; + my $meta = Args::get_meta('meta'); + + my $img = $Caseman::vol2path{$vol}; + my $offset = $Caseman::vol2start{$vol}; + my $imgtype = $Caseman::vol2itype{$vol}; + + my $fname = ""; + my $type = ""; + + if (exists $Args::args{'dir'}) { + $Args::args{'dir'} .= "/" + if ($Args::args{'dir'} eq ""); + $fname = "$mnt$Args::args{'dir'}"; + + if (($Args::args{'dir'} =~ /\/$/) || ($Args::args{'dir'} eq "")) { + Print::log_host_inv( + "$img_sh: Creating note for directory $fname ($meta)"); + $type = "dir"; + } + else { + Print::log_host_inv( + "$img_sh: Creating note for file $fname ($meta)"); + $type = "file"; + } + } + + else { + Print::log_host_inv( + "$img_sh: Creating note for $Fs::meta_str{$ftype} $meta"); + $type = "$Fs::meta_str{$ftype}"; + } + + Print::print_html_header("Writing a note / event"); + + # Get the times for the meta + # Set the timezone to the host zone + if ("$Caseman::tz" ne "") { + $ENV{TZ} = "$Caseman::tz"; + POSIX::tzset(); + } + + my $meta_int = $meta; + $meta_int = $1 if ($meta_int =~ /^(\d+)-\d+(-\d+)?$/); + local *OUT; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ils' -s $Caseman::ts -f $ftype -e -o $offset -i $imgtype $img $meta_int" + ); + + # Skip to the fourth line + my $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + $tmp = Exec::read_pipe_line(*OUT); + unless ((defined $tmp) + && ($tmp =~ /^$::REG_META\|\w\|\d+\|\d+\|(\d+)\|(\d+)\|(\d+)\|/o)) + { + Print::print_err("Error parsing 'ils' output"); + } + my $mtime = $1; + my $atime = $2; + my $ctime = $3; + close(OUT); + + # Create a "normal" note + if ((exists $Args::args{'norm_note'}) && ($Args::args{'norm_note'} == 1)) { + + my $notes_file = investig_notes_fname(); + open NOTES, ">>$notes_file" or die "Can't open log: $notes_file"; + print "Note added to $notes_file:<p>\n\n"; + + # Date + my $tmp = localtime(); + print NOTES "$tmp\n"; + print "$tmp\n"; + + # We have a file name + if ($fname ne "") { + if ($type eq 'dir') { + print NOTES "Directory: $fname\n"; + print "Directory: $fname<br>\n"; + } + else { + print NOTES "File: $fname\n"; + print "File: $fname<br>\n"; + } + } + if ($meta ne "") { + print NOTES "Volume: $vol Meta: $meta\n"; + print "Volume: $vol Meta: $meta<br>\n"; + } + print NOTES "M-time: " . localtime($mtime) . "\n"; + print "M-time: " . localtime($mtime) . "<br>\n"; + print NOTES "A-time: " . localtime($atime) . "\n"; + print "A-time: " . localtime($atime) . "<br>\n"; + print NOTES "C-time: " . localtime($ctime) . "\n"; + print "C-time: " . localtime($ctime) . "<br>\n"; + + # The actual notes and a line at the bottom + print NOTES "\n$Args::args{'note'}\n\n" . "-" x 70 . "\n"; + print "<p>".Print::html_encode($Args::args{'note'})."<p>"; + + close(NOTES); + } + + # Create a sequencer event - if there are any + unless (((exists $Args::args{'mtime'}) && ($Args::args{'mtime'} == 1)) + || ((exists $Args::args{'atime'}) && ($Args::args{'atime'} == 1)) + || ((exists $Args::args{'ctime'}) && ($Args::args{'ctime'} == 1))) + { + + print "<hr>\n" + . "You can view the notes from the Host Manager View<p>" + . "<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_NORM\">" + . "<img border=0 src=\"pict/menu_b_note.jpg\" " + . "width=\"167\" height=20 alt=\"View Notes\"></a>\n"; + + Print::print_html_footer(); + + return 0; + } + + # Get rid of the carriage returns + $Args::args{'note'} =~ s/\n/<br>/gs; + + my $notes_file = investig_seq_notes_fname(); + open NOTES, ">>$notes_file" or die "Can't open log: $notes_file"; + + if ((exists $Args::args{'mtime'}) && ($Args::args{'mtime'} == 1)) { + + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime($mtime); + $year += 1900; + $mon += 1; + $mday = "0$mday" if ($mday < 10); + $hour = "0$hour" if ($hour < 10); + $min = "0$min" if ($min < 10); + $sec = "0$sec" if ($sec < 10); + + print NOTES "'$year','$mon','$mday','$hour','$min','$sec'," + . "'$Args::args{'host'}','$vol','$fname','$meta',''," + . "'$type','[M-Time]$Args::args{'note'}'\n"; + + Print::log_host_inv( + "$img_sh: M-Time note added for meta $Args::args{'meta'}"); + print "M-Time sequence event added<br>\n"; + } + + if ((exists $Args::args{'atime'}) && ($Args::args{'atime'} == 1)) { + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime($atime); + $year += 1900; + $mon += 1; + $mday = "0$mday" if ($mday < 10); + $hour = "0$hour" if ($hour < 10); + $min = "0$min" if ($min < 10); + $sec = "0$sec" if ($sec < 10); + + print NOTES "'$year','$mon','$mday','$hour','$min','$sec'," + . "'$Args::args{'host'}','$vol','$fname','$meta',''," + . "'$type','[A-Time]$Args::args{'note'}'\n"; + + Print::log_host_inv( + "$img_sh: A-Time note added for meta $Args::args{'meta'}"); + print "A-Time sequence event added<br>\n"; + } + + if ((exists $Args::args{'ctime'}) && ($Args::args{'ctime'} == 1)) { + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime($ctime); + $year += 1900; + $mon += 1; + $mday = "0$mday" if ($mday < 10); + $hour = "0$hour" if ($hour < 10); + $min = "0$min" if ($min < 10); + $sec = "0$sec" if ($sec < 10); + + print NOTES "'$year','$mon','$mday','$hour','$min','$sec'," + . "'$Args::args{'host'}','$vol','$fname','$meta',''," + . "'$type','[C-Time]$Args::args{'note'}'\n"; + + Print::log_host_inv( + "$img_sh: C-Time note added for meta $Args::args{'meta'}"); + print "C-Time sequence event added<br>\n"; + } + + close(NOTES); + + print "<p><hr>\n" + . "You can view the notes and events from the Host Manager View<p>"; + + if ((exists $Args::args{'norm_note'}) && ($Args::args{'norm_note'} == 1)) { + print +"<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_NORM\">" + . "<img border=0 src=\"pict/menu_b_note.jpg\" " + . "width=\"167\" height=20 alt=\"View Notes\"></a>\n"; + } + + print +"<a href=\"$::PROGNAME?${Args::baseargs_novol}&mod=$::MOD_NOTES&view=$Notes::READ_SEQ\">" + . "<img border=0 src=\"pict/menu_b_seq.jpg\" width=\"167\" height=\"20\" " + . " alt=\"Event Sequencer\"></a>\n"; + + Print::print_html_footer(); + + return 0; +} + +# Display the contents of the "normal" notes file +sub read_norm { + Print::print_html_header("Contents of Notes File"); + + my $notes_file = investig_notes_fname(); + + Print::log_host_inv("Viewing contents of notes file ($notes_file)"); + if ((!(-e "$notes_file")) || (-z "$notes_file")) { + print "No notes have been entered yet.<br>\n" + . "They can be entered using the <i>Add Note</i> link within each analysis mode.<br>\n"; + return; + } + + open NOTES, "<$notes_file" or die "Can't open log: $notes_file"; + + my $file = ""; + my $dir = ""; + + print "<table width=100%>\n" + . "<tr bgcolor=$::BACK_COLOR><td align=left>\n"; + + my $row = 0; + + # This will need to change whenever the log format changes + while (<NOTES>) { + + # we need to extract mnt from here + $file = $1 if (/^File: (.*)$/); + $dir = $1 if (/^Directory: (.*)$/); + + # Reset the $file if we are at the end of the current note + if (/^\-+$/) { + $file = ""; + $dir = ""; + if (($row++ % 2) == 0) { + print +"</td></tr>\n<tr bgcolor=$::BACK_COLOR_TABLE><td align=left>\n"; + } + else { + print "</td></tr>\n<tr bgcolor=$::BACK_COLOR><td align=left>\n"; + } + } + else { + print Print::html_encode($_); + } + + if (/^Volume: ($::REG_VNAME) Meta: ([0-9\-]+)/o) { + $vol = $1; + $meta = $2; + next unless (exists $Caseman::vol2cat{$vol}); + + # file note + if ($file ne "") { + + # extract the prepended mnt value + my $mnt = $Caseman::vol2mnt{$vol}; + my $fname = ""; + $fname = $1 if ($file =~ /^$mnt\/?(.*)$/); + print "<a href=\"$::PROGNAME?$Args::baseargs_novol" + . "&mod=$::MOD_FILE&view=$File::CONT_FR" + . "&vol=$vol&meta=$meta&dir=$fname\" target=\"_blank\">" + . "<img src=\"pict/but_view.jpg\" alt=\"view\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></a><br>\n"; + } + + # directory note + elsif ($dir ne "") { + + # extract the prepended mnt value + my $mnt = $Caseman::vol2mnt{$vol}; + my $fname = ""; + $fname = $1 if ($dir =~ /^$mnt\/?(.*)$/); + print "<a href=\"$::PROGNAME?$Args::baseargs_novol" + . "&mod=$::MOD_FRAME&submod=$::MOD_FILE&vol=$vol&" + . "&meta=$meta&dir=$fname\" target=\"_blank\">" + . "<img src=\"pict/but_view.jpg\" alt=\"view\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></a><br>\n"; + + } + + # meta note + else { + print "<a href=\"$::PROGNAME?$Args::baseargs_novol" + . "&mod=$::MOD_FRAME&submod=$::MOD_META&vol=$vol" + . "&meta=$meta\" target=\"_blank\">" + . "<img src=\"pict/but_view.jpg\" alt=\"view\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></a><br>\n"; + + } + } + + # block note + elsif (/^Volume: ($::REG_VNAME) \w+: ([0-9]+) Len: (\d+)/o) { + $vol = $1; + $blk = $2; + $len = $3; + next unless (exists $Caseman::vol2cat{$vol}); + + print "<a href=\"$::PROGNAME?$Args::baseargs_novol" + . "&mod=$::MOD_FRAME&submod=$::MOD_DATA&vol=$vol" + . "&block=$blk&len=$len\" target=\"_blank\">" + . "<img src=\"pict/but_view.jpg\" alt=\"view\" " + . "width=45 height=22 alt=\"View\" border=\"0\"></a><br>\n"; + + } + } + + print "</tr></table>\n"; + + # Ok and refresh buttons + print "<p><center><table width=600>\n" + . "<tr><td width=300 align=center>\n" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&view=$Caseman::VOL_OPEN&$Args::baseargs\">" + . "<img border=0 src=\"pict/menu_b_close.jpg\" width=167 height=20 alt=\"close\"></a>\n" + . "</td><td width=300 align=center>\n" + . "<a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::READ_NORM&$Args::baseargs\">" + . "<img border=0 src=\"pict/menu_b_ref.jpg\" width=167 height=20 alt=\"refresh\"></a>\n" + . "</td></tr></table>\n"; + + close(NOTES); + + Print::print_html_footer(); + + return 0; +} + +######################################################################### +# Sequencer Code + +# Write a sequence event that was manually entered +sub write_seq_man { + + Args::check_note(); + + Print::print_html_header("Writing Sequencer Event"); + + # Get rid of carriage returns that Netscape adds + $Args::args{'note'} =~ tr/\r//d; + $Args::args{'note'} =~ s/\n/<br>/gs; + + if ($Args::args{'note'} eq "") { + print( "A comment must be given for the event<br>\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::READ_SEQ&$Args::baseargs\">" + . "<img border=0 src=\"pict/menu_b_ok.jpg\" " + . "width=167 height=20></a>"); + Print::print_err("\n"); + } + + # Check the args and add them to the final string that will be written + my $str = ""; + unless ((exists $Args::args{'year'}) + && ($Args::args{'year'} =~ /^(\d\d\d\d)$/)) + { + Print::print_err("Invalid year"); + } + $str .= "'$1',"; + + unless ((exists $Args::args{'mon'}) && ($Args::args{'mon'} =~ /^(\d\d?)$/)) + { + Print::print_err("Invalid month"); + } + $str .= "'$1',"; + + unless ((exists $Args::args{'day'}) && ($Args::args{'day'} =~ /^(\d\d?)$/)) + { + Print::print_err("Invalid day"); + } + $str .= "'$1',"; + + unless ((exists $Args::args{'hour'}) + && ($Args::args{'hour'} =~ /^(\d\d?)$/)) + { + Print::print_err("Invalid hour"); + } + $str .= "'$1',"; + + unless ((exists $Args::args{'min'}) && ($Args::args{'min'} =~ /^(\d\d?)$/)) + { + Print::print_err("Invalid min"); + } + $str .= "'$1',"; + + unless ((exists $Args::args{'sec'}) && ($Args::args{'sec'} =~ /^(\d\d?)$/)) + { + Print::print_err("Invalid sec"); + } + $str .= "'$1',"; + + # There are no image, meta, file name, or data unit for this type + $str .= "'$Args::args{'host'}','','','','',"; + + unless ((exists $Args::args{'src'}) && ($Args::args{'src'} =~ /^(\w+)$/)) { + Print::print_err("Invalid src"); + } + $str .= "'$1','$Args::args{'note'}'\n"; + + # Write the string to the notes file + my $notes_file = investig_seq_notes_fname(); + open NOTES, ">>$notes_file" or die "Can't open log: $notes_file"; + print NOTES $str; + close(NOTES); + + # Send a message to the user + print "Event Added to Sequencer file:<br><br>\n" + . "$::d2m[$Args::args{'mon'}] $Args::args{'day'}, $Args::args{'year'} " + . "$Args::args{'hour'}:$Args::args{'min'}:$Args::args{'sec'}<br><br>\n" + . Print::html_encode($Args::args{'note'})."<br>\n" + . "<p><a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::READ_SEQ&$Args::baseargs&" + . "year=$Args::enc_args{'year'}&mon=$Args::enc_args{'mon'}&day=$Args::enc_args{'day'}&" + . "hour=$Args::enc_args{'hour'}&min=$Args::enc_args{'min'}&sec=$Args::enc_args{'sec'}\">" + . "<img border=0 src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=43 height=20></a>\n"; + + Print::print_html_footer(); + return 0; +} + +# View the sequencer file +sub read_seq { + + Print::print_html_header("Event Sequencer"); + + print "<center>\n" . "<h3>Event Sequencer</h3>\n"; + + my $cnt = 0; + my @entry; + my ( + @year, @mon, @day, @hour, @min, @sec, @host, + @vol, @fname, @meta, @data, @type, @note + ); + + # Read the sequencer file into arrays that will be sorted + my $notes_file = investig_seq_notes_fname(); + if (-e $notes_file) { + + open NOTES, "$notes_file" or die "Can't open log: $notes_file"; + while (<NOTES>) { + + unless ( +/^'?(\d+)'?,'?(\d+)'?,'?(\d+)'?,'?(\d+)'?,'?(\d+)'?,'?(\d+)'?,'?($::REG_HOST)'?,'?($::REG_VNAME)?'?,'?(.*?)'?,'?($::REG_META)?'?,'?(\d+)?'?,'?([\w\s]+)'?,'?(.*?)'?$/ + ) + { + Print::print_err("Error parsing sequence event entry: $_"); + } + + $year[$cnt] = $1; + $mon[$cnt] = $2; + $day[$cnt] = $3; + $hour[$cnt] = $4; + $min[$cnt] = $5; + $sec[$cnt] = $6; + $host[$cnt] = $7; + $vol[$cnt] = $8; + $fname[$cnt] = ""; + $fname[$cnt] = $9 if (defined $9); + $meta[$cnt] = ""; + $meta[$cnt] = $10 if (defined $10); + $data[$cnt] = ""; + $data[$cnt] = $11 if (defined $11); + $type[$cnt] = $12; + $note[$cnt] = $13; + + $entry[$cnt] = $cnt; + $cnt++; + } + + close(NOTES); + + # Sort the values by date, source, and then note + my @sorted = sort { + $year[$a] <=> $year[$b] + or $mon[$a] <=> $mon[$b] + or $day[$a] <=> $day[$b] + or $hour[$a] <=> $hour[$b] + or $min[$a] <=> $min[$b] + or $sec[$a] <=> $sec[$b] + or lc($type[$a]) cmp lc($type[$b]) + or lc($note[$a]) cmp lc($note[$b]) + } @entry; + + # Table and header + print "<table width=800 border=1>\n" + . "<tr background=\"$::YEL_PIX\">\n" + . "<th>Date & Time</th>\n" + . "<th>Source</th>\n" + . "<th>Event & Note</th></tr>\n"; + + # Cycle through the sorted events + my $row = 0; + foreach my $i (@sorted) { + + # Alternate row colors + if (($row % 2) == 0) { + print "<tr bgcolor=\"$::BACK_COLOR\">\n"; + } + else { + print "<tr bgcolor=\"$::BACK_COLOR_TABLE\">\n"; + } + + # Date & Time + print "<td align=left valign=top>\n" + . "$::d2m[$mon[$i]] $day[$i], $year[$i]" + . " $hour[$i]:$min[$i]:$sec[$i]</td>" + . "<td align=left valign=top>\n"; + + # If there is as name, then we will show it + # @@@ Why does an error message come up from here: + # Use of uninitialized value in string ne at + if ($fname[$i] ne "") { + + if ( (exists $vol[$i]) + && (defined $vol[$i]) + && ($vol[$i] ne "") + && (exists $Caseman::vol2mnt{$vol[$i]}) + && (exists $meta[$i])) + { + + # extract the prepended mnt value + my $mnt = $Caseman::vol2mnt{$vol[$i]}; + my $fname = ""; + $fname = $1 if ($fname[$i] =~ /^$mnt\/?(.*)$/); + + # Check if it is a directory + if ($type[$i] eq 'dir') { + print "<a href=\"$::PROGNAME?mod=$::MOD_FRAME&" + . "submod=$::MOD_FILE&vol=$vol[$i]&$Args::baseargs&meta=$meta[$i]&dir=$fname\" " + . "target=\"_blank\">\n" + . "<tt>$fname[$i]</tt></a>\n"; + } + else { + print "<a href=\"$::PROGNAME?mod=$::MOD_FILE&" + . "view=$File::CONT_FR&vol=$vol[$i]&$Args::baseargs&meta=$meta[$i]&dir=$fname\" " + . "target=\"_blank\">\n" + . "<tt>$fname[$i]</tt></a>\n"; + } + } + else { + print "<tt>$fname[$i]</tt>\n"; + } + } + + # Display the meta value if there was no name + elsif (($vol[$i] ne "") && (defined $meta[$i]) && ($meta[$i] ne "")) + { + my $ftype = $Caseman::vol2ftype{$vol[$i]}; + + # Include a link if we can + if (exists $Caseman::vol2mnt{$vol[$i]}) { + print "<a href=\"$::PROGNAME?mod=$::MOD_FRAME&" + . "submod=$::MOD_META&vol=$vol[$i]" + . "&$Args::baseargs&meta=$meta[$i]\" target=\"_blank\">\n" + . "$Fs::meta_str{$ftype}: $meta[$i]</a>\n"; + } + else { + print "$Fs::meta_str{$ftype}: $meta[$i]\n"; + } + } + + # Otherwise, just give the source type + else { + print "$type[$i]\n"; + } + print "</td>\n"; + + # Print the actual note + $note[$i] = Print::html_encode($note[$i]); + $note[$i] = " " if ($note[$i] eq ""); + print "<td align=left>$note[$i]</td></tr>\n"; + + $row++; + } + + print "</table>\n"; + + } + + # End of if file exists + else { + print "No events currently exist<br>\n"; + } + + # Ok and refresh buttons + print "<p><table width=600>\n" + . "<tr><td width=300 align=center>\n" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_OPEN&$Args::baseargs\">" + . "<img border=0 src=\"pict/menu_b_close.jpg\" width=167 height=20 alt=\"close\"></a>\n" + . "</td><td width=300 align=center>\n" + . "<a href=\"$::PROGNAME?mod=$::MOD_NOTES&" + . "view=$Notes::READ_SEQ&$Args::baseargs\">" + . "<img border=0 src=\"pict/menu_b_ref.jpg\" width=167 height=20 alt=\"refresh\"></a>\n" + . "</td></tr></table>\n"; + + # Manually add a new event + print "<hr>\n" + . "<b>Add a New Event</b><br><br>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<textarea rows=10 cols=50 wrap=\"virtual\" name=\"note\">" + . "</textarea><br>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_NOTES\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Notes::WRITE_SEQ_MAN\">\n" + . Args::make_hidden(); + + # Month + print "<table><tr><td align=\"left\">\n"; + print "<p>Date: " . "<select name=\"mon\" size=\"1\">\n"; + + my $prev = 1; + $prev = $Args::args{'mon'} + if ((exists $Args::args{'mon'}) && ($Args::args{'mon'} =~ /^\d+$/)); + + for my $i (1 .. 12) { + if ($i == $prev) { + print "<option value=$i selected>$::d2m[$i]</option>\n"; + } + else { + print "<option value=\"$i\">$::d2m[$i]</option>\n"; + } + } + + print "</select>\n"; + + # Day + print " <select name=\"day\" size=\"1\">\n"; + + $prev = 1; + $prev = $Args::args{'day'} + if ((exists $Args::args{'day'}) && ($Args::args{'day'} =~ /^\d+$/)); + + for my $i (1 .. 31) { + my $dstr = $i; + $dstr = "0$i" if ($i < 10); + + if ($prev eq $dstr) { + print "<option value=\"$dstr\" selected>$dstr</option>\n"; + } + else { + print "<option value=\"$dstr\">$dstr</option>\n"; + } + } + print "</select>\n"; + + # Year + $prev = 1900 + (localtime())[5]; + $prev = $Args::args{'year'} + if ((exists $Args::args{'year'}) && ($Args::args{'year'} =~ /^\d+$/)); + + print " <input type=\"text\" value=\"$prev\" " + . "name=\"year\" size=\"6\">\n"; + + # Hour + print " <select name=\"hour\" size=\"1\">\n"; + $prev = 0; + $prev = $Args::args{'hour'} + if ((exists $Args::args{'hour'}) && ($Args::args{'hour'} =~ /^\d+$/)); + + for my $i (0 .. 23) { + my $hstr = $i; + $hstr = "0$i" if ($i < 10); + + if ($prev eq $hstr) { + print "<option value=\"$hstr\" selected>$hstr</option>\n"; + } + else { + print "<option value=\"$hstr\">$hstr</option>\n"; + } + } + print "</select>\n"; + + # Min + print ":<select name=\"min\" size=\"1\">\n"; + $prev = 0; + $prev = $Args::args{'min'} + if ((exists $Args::args{'min'}) && ($Args::args{'min'} =~ /^\d+$/)); + + for my $i (0 .. 59) { + my $mstr = $i; + $mstr = "0$i" if ($i < 10); + if ($prev eq $mstr) { + print "<option value=\"$mstr\" selected>$mstr</option>\n"; + } + else { + print "<option value=\"$mstr\">$mstr</option>\n"; + } + } + print "</select>\n"; + + # Sec + print ":<select name=\"sec\" size=\"1\">\n"; + $prev = 0; + $prev = $Args::args{'sec'} + if ((exists $Args::args{'sec'}) && ($Args::args{'sec'} =~ /^\d+$/)); + + for my $i (0 .. 59) { + my $sstr = $i; + $sstr = "0$i" if ($i < 10); + + if ($prev eq $sstr) { + print "<option value=\"$sstr\" selected>$sstr</option>\n"; + } + else { + print "<option value=\"$sstr\">$sstr</option>\n"; + } + } + print "</select></td></tr>\n" . "<tr><td> </td></tr>\n"; + + # Type + print "<tr><td align=\"left\">Event Source: <select name=\"src\" size=1>\n" + . "<option value=\"firewall\">firewall</option>\n" + . "<option value=\"ids\">ids</option>\n" + . "<option value=\"isp\">isp</option>\n" + . "<option value=\"log\">log</option>\n" + . "<option value=\"other\" selected>other</option>\n" + . "<option value=\"person\">person</option>\n" + . "</select></td></tr>\n" + . "<tr><td> </td></tr>\n"; + + print +"<tr><td align=\"center\"><input type=\"image\" src=\"pict/menu_b_add.jpg\" " + . "width=167 height=20 alt=\"Add\" border=\"0\">\n</form></td></tr></table>\n"; + + Print::print_html_footer(); + return 0; +} + +# Conver the 'image' format to the 'volume' format +sub convert { + my %img2vol = %{shift()}; + + my @invs = Caseman::read_invest(); + if (scalar @invs == 0) { + push @invs, "unknown"; + } + + foreach $i (@invs) { + my $notes_file = "$::host_dir" . "$::LOGDIR/$i.notes"; + + if ((!(-e "$notes_file")) || (-z "$notes_file")) { + next; + } + Print::log_host_info( + "Converting format of notes file for $i ($notes_file)"); + + open NOTES, "<$notes_file" or die "Can't open log: $notes_file"; + + my $notes_file_new = $notes_file . ".new"; + open NOTES_NEW, ">$notes_file_new" + or die "Can't open writing log: $notes_file_new"; + + while (<NOTES>) { + + if (/Image: ($::REG_IMG)\s+(.*)$/) { + my $img = $1; + my $addr = $2; + + unless (exists $img2vol{$img}) { + print STDERR +"Error finding image during notes conversion: $img. Not converting\n"; + next; + } + my $vol = $img2vol{$img}; + + # Convert old description to last versions + $addr =~ s/Inode:/Meta:/; + print NOTES_NEW "Volume: $vol $addr\n"; + } + else { + print NOTES_NEW $_; + } + } + + close(NOTES); + close(NOTES_NEW); + rename $notes_file, $notes_file . ".bak"; + rename $notes_file_new, $notes_file; + } + + # NOw do sequence notes + foreach $i (@invs) { + my $notes_file = "$::host_dir" . "$::LOGDIR/$i.seq.notes"; + if ((!(-e "$notes_file")) || (-z "$notes_file")) { + next; + } + + open NOTES, "$notes_file" or die "Can't open log: $notes_file"; + + $notes_file_new = $notes_file . ".new"; + open NOTES_NEW, ">$notes_file_new" + or die "Can't open log for updating: $notes_file_new"; + + while (<NOTES>) { + + # No image in entry + if (/^'\d+','\d+','\d+','\d+','\d+','\d+','$::REG_HOST','','/) { + print NOTES_NEW $_; + } + elsif ( +/^('\d+','\d+','\d+','\d+','\d+','\d+','$::REG_HOST',')($::REG_IMG)(','.*)$/ + ) + { + my $pre = $1; + my $img = $2; + my $post = $3; + unless (exists $img2vol{$img}) { + print STDERR +"Error finding image during notes conversion: $img. Not converting\n"; + next; + } + my $vol = $img2vol{$img}; + print NOTES_NEW $pre . $vol . $post . "\n"; + } + else { + print NOTES_NEW "$_"; + return; + } + } + + close(NOTES); + close(NOTES_NEW); + rename $notes_file, $notes_file . ".bak"; + rename $notes_file_new, $notes_file; + } + + return 0; +} diff --git a/lib/Print.pm b/lib/Print.pm new file mode 100644 index 0000000000000000000000000000000000000000..417caa70f4646bd8fb4e8367473ee2c6f2be5652 --- /dev/null +++ b/lib/Print.pm @@ -0,0 +1,390 @@ +package Print; + +# +# Utilities to print information +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Escape HTML entities +# Converts \n to <br>\n +sub html_encode { + my $text = shift; + $text =~ s/&/&/gs; + $text =~ s/</</gs; + $text =~ s/>/>/gs; + $text =~ s/\"/"/gs; + $text =~ s/\n/<br>\n/gs; + + # @@@ LEADING SPACES and TABS + # while ($text =~ s/^( )*\t/"$1 "/eig) {} + # while ($text =~ s/^( )* /"$1 "/eig) {} + return $text; +} + +# remove control chars from printout +# this does not escape HTML entities, so you can pass this HTML code +sub print_output { + my $out = shift; + print "$out"; + + while (my $out = shift) { + foreach $_ (split(//, $out)) { + if ( ($_ eq "\n") + || ($_ eq "\r") + || ($_ eq "\f") + || ($_ eq "\t")) + { + print "$_"; + } + elsif ((ord($_) < 0x20) && (ord($_) >= 0x00)) { + print "^" . ord($_); + } + else { + print "$_"; + } + } + } +} + +# Added to provide output in hexdump format +# function gets called on a per-icat basis, +# The offset value is the byte offset that this data +# starts at, since the File.pm code calls it in 1024 +# byte chunks) +sub print_hexdump { + my $out = shift; # data to output + my $offset = shift; # starting byte offset in file + my $buf = ""; + + foreach $i (split(//, $out)) { + my $idx = $offset % 16; + + if ($idx == 0) { + printf("%08X: ", $offset); + } + + printf("%02X", ord($i)); + if (($idx % 2) == 1) { + printf(" "); + } + + $buf[$idx] = $i; + + if ($idx == 15) { + print " "; + for (my $j = 0; $j < 16; $j++) { + if ($buf[$j] =~ m/[ -~]/) { + print $buf[$j]; + } + else { + print "."; + } + $buf[$j] = 0; + } + print "\n"; + } + $offset++; + } + + # print out last line if < 16 bytes long + my $l = $offset % 16; + + if ($l) { + my $t = (16 - $l) * 2 + (16 - $l) / 2; + for (my $j = 0; $j < $t; $j++) { + print " "; + } + print " "; + for (my $j = 0; $j < $l; $j++) { + if ($buf[$j] =~ m/[ -~]/) { + print $buf[$j]; + } + else { + print "."; + } + } + print "\n"; + } +} + +############################################ +# +# HTTP/HTML Headers and Footers + +# The page that makes the frameset does not have a body statement +# This routine is used to make the minimum required header statements +sub print_html_header_frameset { + my $text = shift; + print "Content-Type: text/html; charset=utf-8$::HTTP_NL$::HTTP_NL"; + + my $time = localtime(); + + print <<EOF; +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<!-- Autopsy ver. $::VER Forensic Browser --> +<!-- Page created at: $time --> +<head> + <title>$text</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <link rel="stylesheet" href="global.css"> +</head> + +EOF +} + +sub print_html_footer_frameset { + print "\n</html>\n" . "$::HTTP_NL$::HTTP_NL"; +} + +# Create the header information with the body tag +sub print_html_header { + print_html_header_frameset(shift); + print "<body bgcolor=\"$::BACK_COLOR\">\n\n"; + print "<link rel=\"SHORTCUT ICON\" href=\"pict/favicon.ico\">\n"; +} + +sub print_html_footer { + print "\n</body>\n</html>\n" . "$::HTTP_NL$::HTTP_NL"; +} + +# Print the header with the margins set to 0 so that the tab buttons +# are flush with the edges of the frame +sub print_html_header_tabs { + print_html_header_frameset(shift); + print "<body marginheight=0 marginwidth=0 topmargin=0 " + . "leftmargin=0 rightmargin=0 botmargin=0 bgcolor=\"$::BACK_COLOR\">\n\n"; + print "<link rel=\"SHORTCUT ICON\" href=\"pict/favicon.ico\">\n"; + $is_body = 1; +} + +sub print_html_footer_tabs { + print "\n</body>\n</html>\n" . "$::HTTP_NL$::HTTP_NL"; +} + +# Header for front page to warn about java script +sub print_html_header_javascript { + my $text = shift; + print "Content-Type: text/html; charset=utf-8$::HTTP_NL$::HTTP_NL"; + + my $time = localtime(); + + # The write line has to stay on one line + print <<EOF; +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<!-- Autopsy ver. $::VER Forensic Browser --> +<!-- Page created at: $time --> +<head> + <title>$text</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <link rel="stylesheet" href="global.css"> + <script language=\"JavaScript\"> + <!-- hide script from old browsers + document.write(\'<center><font color=\"red\"><p>WARNING: Your browser currently has Java Script enabled.</font><p>You do not need Java Script to use Autopsy and it is recommended that it be turned off for security reasons.<hr></center>\'); + //--> + </script> +</head> + +<body bgcolor=\"$::BACK_COLOR\"> +<link rel=\"SHORTCUT ICON\" href=\"pict/favicon.ico\"> + +EOF +} + +sub print_html_footer_javascript { + print "\n</body>\n</html>\n" . "$::HTTP_NL$::HTTP_NL"; +} + +# For raw text outputs (Pass the name of a file if it is being saved) +sub print_text_header { + print "Content-Type: text/plain; charset=utf-8$::HTTP_NL"; + if (scalar @_ > 0) { + my $fname = shift(); + print "Content-Disposition: inline; " . "filename=$fname;$::HTTP_NL"; + } + print "$::HTTP_NL"; +} + +sub print_text_footer { + print "$::HTTP_NL$::HTTP_NL"; +} + +# For forced save outputs +sub print_oct_header { + print "Content-Type: application/octet-stream$::HTTP_NL"; + if (scalar @_ > 0) { + my $fname = shift(); + print "Content-Disposition: inline; " . "filename=$fname;$::HTTP_NL"; + } + print "$::HTTP_NL"; +} + +sub print_oct_footer { +} + +# Error message that is used when an HTTP/HTML header is needed +# This escapes the characters that chould be HTML entities. +# it will also replace \n with <br> and other things that html_encode() +# can do. Do not send arbitrary HTML to this function. +sub print_check_err { + print_html_header(""); + print html_encode(shift()) . "<br>\n"; + print_html_footer(); + sleep(1); + exit 1; +} + +# Error message when header already exists +# This escapes the characters that chould be HTML entities. +# it will also replace \n with <br> and other things that html_encode() +# can do. Do not send arbitrary HTML to this function. +sub print_err { + print html_encode(shift()) . "<br>\n"; + sleep(1); + print_html_footer(); + exit 1; +} + +################################################################## +# +# Logging +# +# + +sub investig_log_fname { + return "" unless (defined $::host_dir && $::host_dir ne ""); + return "" unless (exists $Args::args{'inv'} && $Args::args{'inv'} ne ""); + + return "$::host_dir" . "$::LOGDIR/$Args::args{'inv'}.log"; +} + +sub investig_exec_log_fname { + return "" unless (defined $::host_dir && $::host_dir ne ""); + return "" unless (exists $Args::args{'inv'} && $Args::args{'inv'} ne ""); + + return "$::host_dir" . "$::LOGDIR/$Args::args{'inv'}.exec.log"; +} + +sub host_log_fname { + return "" unless (defined $::host_dir && $::host_dir ne ""); + + return "$::host_dir" . "$::LOGDIR/host.log"; +} + +sub case_log_fname { + return "" unless (defined $::case_dir && $::case_dir ne ""); + + return "$::case_dir" . "case.log"; +} + +# Log data to the investigators specific log file +sub log_host_inv { + return unless ($::USE_LOG == 1); + + my $str = shift; + chomp $str; + + my $date = localtime; + my $fname = investig_log_fname(); + return if ($fname eq ""); + + open HOSTLOG, ">>$fname" or die "Can't open log: $fname"; + print HOSTLOG "$date: $str\n"; + close(HOSTLOG); + + return; +} + +sub log_host_inv_exec { + return unless ($::USE_LOG == 1); + my $str = shift; + chomp $str; + + my $date = localtime; + my $fname = investig_exec_log_fname(); + return if ($fname eq ""); + + open HOSTLOG, ">>$fname" or die "Can't open log: $fname"; + print HOSTLOG "$date: $str\n"; + close(HOSTLOG); + + return; +} + +# log data to the general log file for the host +sub log_host_info { + return unless ($::USE_LOG == 1); + + my $str = shift; + chomp $str; + + my $date = localtime; + my $fname = host_log_fname(); + return if ($fname eq ""); + + open HOSTLOG, ">>$fname" or die "Can't open log: $fname"; + print HOSTLOG "$date: $str\n"; + close(HOSTLOG); + + return; +} + +sub log_case_info { + return unless ($::USE_LOG == 1); + my $str = shift; + chomp $str; + my $date = localtime; + my $fname = case_log_fname(); + return if ($fname eq ""); + + open CASELOG, ">>$fname" or die "Can't open log: $fname"; + print CASELOG "$date: $str\n"; + close(CASELOG); + + return; +} + +sub log_session_info { + return unless ($::USE_LOG == 1); + my $str = shift; + chomp $str; + my $date = localtime; + + my $lname = "autopsy.log"; + open AUTLOG, ">>$::LOCKDIR/$lname" or die "Can't open log: $lname"; + print AUTLOG "$date: $str\n"; + close(AUTLOG); + + return; +} + +1; diff --git a/lib/Timeline.pm b/lib/Timeline.pm new file mode 100644 index 0000000000000000000000000000000000000000..4cd676af939859892f2e9a60a9c2f29054e9791c --- /dev/null +++ b/lib/Timeline.pm @@ -0,0 +1,1425 @@ +# +# Timeline functions +# +# Brian Carrier [carrier@sleuthkit.org] +# Copyright (c) 2001-2005 by Brian Carrier. All rights reserved +# +# This file is part of the Autopsy Forensic Browser (Autopsy) +# +# Autopsy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Autopsy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Autopsy; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package Timeline; + +use POSIX; # needed for tzset + +# Changing the order of this may affect the main function ordering + +$Timeline::BLANK = 0; +$Timeline::FRAME = 1; +$Timeline::TABS = 2; +$Timeline::BODY_ENTER = 3; +$Timeline::BODY_RUN = 4; +$Timeline::TL_ENTER = 5; +$Timeline::TL_RUN = 6; +$Timeline::VIEW_FR = 7; +$Timeline::VIEW_MENU = 8; +$Timeline::VIEW_IDX = 9; +$Timeline::VIEW_SUM = 10; +$Timeline::VIEW = 11; + +# Types of modes for fname (i.e. can we overwrite it if it exists) +my $FNAME_MODE_INIT = 0; +my $FNAME_MODE_OVER = 1; + +sub main { + + return if ($::LIVE == 1); + + # By default, show the main frame + $Args::args{'view'} = $Args::enc_args{'view'} = $Timeline::FRAME + unless (exists $Args::args{'view'}); + + Args::check_view(); + my $view = Args::get_view(); + + if ($view < $Timeline::VIEW_FR) { + if ($view == $Timeline::BLANK) { + return blank(); + } + elsif ($view == $Timeline::FRAME) { + return frame(); + } + elsif ($view == $Timeline::TABS) { + return tabs(); + } + elsif ($view == $Timeline::BODY_ENTER) { + return body_enter(); + } + elsif ($view == $Timeline::BODY_RUN) { + return body_run(); + } + elsif ($view == $Timeline::TL_ENTER) { + return tl_enter(); + } + elsif ($view == $Timeline::TL_RUN) { + return tl_run(); + } + } + else { + if ($view == $Timeline::VIEW_FR) { + return view_fr(); + } + elsif ($view == $Timeline::VIEW_MENU) { + return view_menu(); + } + elsif ($view == $Timeline::VIEW_IDX) { + return view_idx(); + } + elsif ($view == $Timeline::VIEW_SUM) { + return view_sum(); + } + elsif ($view == $Timeline::VIEW) { + return view(); + } + } + Print::print_check_err("Invalid Timeline View"); +} + +# Call the appropriate function based on the value of sort +sub frame { + Print::print_html_header_frameset( + "Timeline: $Args::args{'case'}:$Args::args{'host'}"); + + print "<frameset rows=\"38,*\">\n"; + + my $submod = $Timeline::BLANK; + $submod = Args::get_submod() if (exists $Args::args{'submod'}); + + # Listing + print "<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::TABS&" + . "$Args::baseargs&submod=$submod\">\n"; + + my $str = ""; + + # Contents + if ($submod == $Timeline::BLANK) { + print +"<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::BLANK&$Args::baseargs\" " + . "name=\"content\">\n</frameset>\n"; + return; + } + elsif ($submod == $Timeline::TL_ENTER) { + $str .= "&body=$Args::args{'body'}" if (exists $Args::args{'body'}); + } + elsif ($submod == $Timeline::VIEW_FR) { + $str .= "&tl=$Args::args{'tl'}" if (exists $Args::args{'tl'}); + } + + print +"<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$submod&$Args::baseargs$str\" " + . "name=\"content\">\n</frameset>\n"; + + Print::print_html_footer_frameset(); + return 0; +} + +# The tabs / button images in timeline view +sub tabs { + Args::check_submod(); + Print::print_html_header_tabs("Timeline Mode Tabs"); + + my $submod = Args::get_submod(); + + print "<center><table width=\"800\" border=\"0\" " + . "cellspacing=\"0\" cellpadding=\"0\"><tr>\n"; + + # Create Datafile + print "<td align=\"center\" width=174>" + . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&" + . "submod=$Timeline::BODY_ENTER&$Args::baseargs\" target=\"_top\">"; + + if ($submod == $Timeline::BODY_ENTER) { + print "<img border=0 " + . "src=\"pict/tl_t_data_cur.jpg\" " + . "width=174 height=38 " + . "alt=\"Create Data File (Current Mode)\"></a>\n"; + } + else { + print "<img border=0 " + . "src=\"pict/tl_t_data_link.jpg\" " + . "width=174 height=38 " + . "alt=\"Create Data File\"></a>\n"; + } + + print "</td>\n" + . "<td align=\"center\" width=174>" + . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&" + . "submod=$Timeline::TL_ENTER&$Args::baseargs\" " + . "target=\"_top\">"; + + # Create Timeline + if ($submod == $Timeline::TL_ENTER) { + print "<img border=0 " + . "src=\"pict/tl_t_tl_cur.jpg\" " + . "width=174 height=38 " + . "alt=\"Create Timeline (Current Mode)\"></a>\n"; + } + else { + print "<img border=0 " + . "src=\"pict/tl_t_tl_link.jpg\" " + . "width=174 height=38 " + . "alt=\"Create Timeline\"></a>\n"; + } + print "</td>\n" + . "<td align=\"center\" width=174>" + . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&" + . "submod=$Timeline::VIEW_MENU&$Args::baseargs\" " + . "target=\"_top\">"; + + # View Timeline + if (($submod == $Timeline::VIEW_FR) || ($submod == $Timeline::VIEW_MENU)) { + print "<img border=0 " + . "src=\"pict/tl_t_view_cur.jpg\" " + . "width=174 height=38 " + . "alt=\"View Timeline (Current Mode)\"></a>\n"; + } + else { + print "<img border=0 " + . "src=\"pict/tl_t_view_link.jpg\" " + . "width=174 height=38 " + . "alt=\"View Timeline\"></a>\n"; + } + + # Notes + print "</td>\n" . "<td align=\"center\" width=174>"; + if ($::USE_NOTES == 1) { + print +"<a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::READ_NORM&$Args::baseargs_novol\" " + . "target=\"_blank\">" + . "<img border=0 " + . "src=\"pict/tl_t_notes_link.jpg\" " + . "width=174 height=38 " + . "alt=\"View Notes\"></a></td>\n"; + } + else { + print "<img border=0 " + . "src=\"pict/tl_t_notes_org.jpg\" " + . "width=174 height=38 " + . "alt=\"View Notes\"></a></td>\n"; + } + + # Help - set to current submod + print "<td align=\"center\" width=52>" + . "<a href=\"$::HELP_URL\" target=\"_blank\">" + . "<img border=0 src=\"pict/tab_help.jpg\" width=52 " + . "alt=\"Help\"></a></td>\n"; + + # Close + print "<td align=\"center\" width=52>" + . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&" + . "view=$Caseman::VOL_OPEN&$Args::baseargs\" target=\"_top\">" + . "<img border=0 src=\"pict/tab_close.jpg\" width=52 " + . "alt=\"Exit to Host Manager\"></a></td>\n" + . "</tr></table>\n"; + + Print::print_html_footer_tabs(); + return 0; +} + +sub body_enter { + Print::print_html_header("Enter Data to Make Body File"); + + my $i; + my %mnt2img; + + # Cycle through each image we read from fsmorgue + foreach $i (keys %Caseman::vol2mnt) { + next + unless ($Caseman::vol2cat{$i} eq "part"); + next + if ( ($Caseman::vol2ftype{$i} eq "swap") + || ($Caseman::vol2ftype{$i} eq "raw")); + $mnt2vol{"$Caseman::vol2mnt{$i}--$i"} = $i; + } + +# sort via parent volume, then starting location, and then mount point (which includes the name) + my @mnt = sort { + ($Caseman::vol2par{$mnt2vol{$a}} cmp $Caseman::vol2par{$mnt2vol{$b}}) + or ($Caseman::vol2start{$mnt2vol{$a}} <=> + $Caseman::vol2start{$mnt2vol{$b}}) + or (lc($a) cmp lc($b)) + } keys %mnt2vol; + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::BODY_RUN\">\n" + . Args::make_hidden() + . "<p>Here we will process the file system images, collect the temporal data, and save the data to a single file." + . "<p>1. Select one or more of the following images to collect data from:\n" + . "<table cellspacing=\"8\" cellpadding=\"2\">"; + + for (my $i = 0; $i <= $#mnt; $i++) { + my $vol = $mnt2vol{$mnt[$i]}; + + print "<tr><td><input type=\"checkbox\" name=\"$vol\" value=\"1\">" + . "</td><td><tt>$Caseman::vol2mnt{$vol}</tt></td><td><tt>$Caseman::vol2sname{$vol}</tt></td>" + . "<td>$Caseman::vol2ftype{$vol}</td>\n"; + } + + print "</table><p>2. Select the data types to gather:<br>\n" + . "<table cellspacing=\"8\" cellpadding=\"2\"><tr>" + . "<td><input type=\"checkbox\" name=\"al_file\" value=\"1\" CHECKED>" + . "</td><td>Allocated Files</td>" + . "<td><input type=\"checkbox\" name=\"unal_file\" value=\"1\" CHECKED>" + . "</td><td>Unallocated Files</td>" + . "</tr></table>\n" + . "<p>3. Enter name of output file (<tt>body</tt>):<br>" + . "<tt>$::DATADIR/</tt>" + . "<input type=\"text\" name=\"fname\" value=\"body\">\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n" + . "<p>4. Generate MD5 Value? " + . "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED>"; + + print "<p><input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\"></form>\n"; + + Print::print_html_footer(); + return 0; +} + +sub body_run { + Args::check_fname(); + Args::check_fname_mode(); + Print::print_html_header("Make Body File"); + + my $fname_rel = Args::get_fname_rel(); + my $fname = Args::get_fname(); + + my $fname_mode = $Args::args{'fname_mode'}; + + if ((-e "$fname") && ($FNAME_MODE_INIT == $fname_mode)) { + print "File Already Exists: $fname_rel\n"; + + my $hidden = Args::make_hidden(); + + $hidden .= + "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::BODY_RUN\">\n"; + + my $i; + foreach $i (%Caseman::vol2mnt) { + $hidden .= "<input type=\"hidden\" name=\"$i\" value=\"1\">\n" + if (exists $Args::args{$i}); + } + + $hidden .= + "<input type=\"hidden\" name=\"al_file\" " + . "value=\"$Args::args{'al_file'}\">\n" + if (exists $Args::args{'al_file'}); + $hidden .= + "<input type=\"hidden\" name=\"unal_file\" " + . "value=\"$Args::args{'unal_file'}\">\n" + if (exists $Args::args{'unal_file'}); + $hidden .= + "<input type=\"hidden\" name=\"md5\" " + . "value=\"$Args::args{'md5'}\">\n" + if (exists $Args::args{'md5'}); + + # Make a new name + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "New Name: <input type=\"text\" name=\"fname\">" + . "<table cellspacing=\"30\" cellpadding=\"2\"><tr><td>" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n" + . "$hidden" + . "<input type=\"image\" src=\"pict/but_new_name.jpg\" " + . "width=79 height=20 alt=\"Use New Name\" border=\"0\">\n" + . "</form></td>\n"; + + # Overwrite it + print "<td><form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"image\" src=\"pict/but_replace.jpg\" " + . "width=66 height=20 alt=\"Replace\" border=\"0\"><br>\n" + . "<input type=\"hidden\" name=\"fname\" value=\"$Args::args{'fname'}\">\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_OVER\">\n" + . "$hidden" + . "</form></td></tr></table>"; + + return 0; + } + + # we will be appending to the file so we should del it now + if (-e "$fname") { + unlink($fname); + } + + my $log_files = ""; + my $log_type = ""; + + # What kind of data are we collecting? + my $al_file = 0; + if (exists $Args::args{'al_file'}) { + $al_file = $Args::args{'al_file'}; + $log_type .= "Allocated Files"; + } + + my $unal_file = 0; + if (exists $Args::args{'unal_file'}) { + $unal_file = $Args::args{'unal_file'}; + $log_type .= ", " if ($log_type ne ""); + $log_type .= "Unallocated Files"; + } + + if (($unal_file == 0) && ($al_file == 0)) { + print + "No data types were selected. You must select at least one.<br>\n"; + return 1; + } + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + my $i; + my $found = 0; + local *OUT; + + # Analyze each image - the image names are passed as an argument + foreach $i (keys %Caseman::vol2mnt) { + if (exists $Args::args{$i}) { + + $found = 1; + my $ftype = $Caseman::vol2ftype{$i}; + my $img = $Caseman::vol2path{$i}; + my $offset = $Caseman::vol2start{$i}; + my $imgtype = $Caseman::vol2itype{$i}; + my $mnt = $Caseman::vol2mnt{$i}; + + $log_files .= ", " if ($log_files ne ""); + $log_files .= "$i"; + + if (($al_file) && ($unal_file)) { + print "Running <tt>fls -r -m</tt> on <tt>$i</tt><br>\n"; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -r -o $offset -i $imgtype $img >> '$fname'" + ); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + } + elsif ($al_file) { + print "Running <tt>fls -ru -m</tt> on <tt>$i</tt><br>\n"; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -ru -o $offset -i $imgtype $img >> '$fname'" + ); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + } + elsif ($unal_file) { + print "Running <tt>fls -rd -m</tt> on <tt>$i</tt><br>\n"; + Exec::exec_pipe(*OUT, +"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -rd -o $offset -i $imgtype $img >> '$fname'" + ); + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + } + } + } + + unless ($found) { + print +"No images were given for analysis. At least one must be selected.<br>\n"; + return 1; + } + + Print::log_host_inv( + "Saving timeline data for $log_type for $log_files to $fname_rel"); + + # append to host config + my $bod_vol = Caseman::add_vol_host_config("body", $fname_rel); + $Caseman::vol2cat{$bod_vol} = "timeline"; + $Caseman::vol2ftype{$bod_vol} = "body"; + $Caseman::vol2itype{$bod_vol} = "raw"; + $Caseman::vol2path{$bod_vol} = $fname; + $Caseman::vol2start{$bod_vol} = 0; + $Caseman::vol2end{$bod_vol} = 0; + $Caseman::vol2sname{$bod_vol} = $fname_rel; + + print "<br>Body file saved to <tt>$fname</tt><br><br>\n" + . "Entry added to host config file<br><br>\n"; + + # Calculate MD5 + if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) { + print "Calculating MD5 Value<br><br>\n"; + my $m = Hash::int_create_wrap($bod_vol); + print "MD5 Value: <tt>$m</tt><br><br>\n"; + } + + print "<p>The next step is to sort the data into a timeline." + . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"body\" value=\"$bod_vol\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$Timeline::TL_ENTER\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return 0; +} + +my $OTYPE_NORM = 1; +my $OTYPE_HOURLY = 2; +my $OTYPE_DAILY = 3; + +sub tl_enter { + Print::print_html_header("Enter data for timeline"); + + my @body; + + # Find the body files if we will be looking for them + unless ((exists $Args::args{'body'}) + && (exists $Caseman::vol2cat{$Args::args{'body'}})) + { + foreach my $k (keys %Caseman::vol2cat) { + if ( ($Caseman::vol2cat{$k} eq "timeline") + && ($Caseman::vol2ftype{$k} eq "body")) + { + push @body, $k; + } + } + + if (scalar(@body) == 0) { + print "There are currently no <tt>body</tt> files " + . "for this host.<br>You must create the intermediate " + . "data file before you can perform this step<br>\n" + . "<p><a target=\"_top\" " + . "href=\"$::PROGNAME?$Args::baseargs&" + . "mod=$::MOD_TL&view=$Timeline::FRAME&" + . "submod=$Timeline::BODY_ENTER\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\">" + . "</a>\n"; + return 1; + } + } + print "Now we will sort the data and save it to a timeline.<p>\n" + . "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n" + . Args::make_hidden() + . "1. Select the data input file (<tt>body</tt>):\n" + . "<table cellspacing=\"0\" cellpadding=\"2\">"; + + # if the body file was specified then just print it + if (exists $Args::args{'body'}) { + print "<tr><td><input type=\"radio\" name=\"body\" " + . "value=\"$Args::args{'body'}\" CHECKED>" + . "</td><td>$Caseman::vol2sname{$Args::args{'body'}}</td>\n"; + } + else { + my @body_sort = sort { lc($a) cmp lc($b) } @body; + my $chk = " CHECKED"; + for (my $i = 0; $i <= $#body_sort; $i++) { + print "<tr><td><input type=\"radio\" name=\"body\" " + . "value=\"$body_sort[$i]\" $chk></td><td>$Caseman::vol2sname{$body_sort[$i]}</td>\n"; + $chk = ""; + } + } + + my $cur_mon = 1 + (localtime())[4]; + my $cur_year = 1900 + (localtime())[5]; + + # STARTING DATE + print "</table>\n" + . "<p>2. Enter the starting date:<br>\n" + . "None: <input type=\"radio\" name=\"st_none\" value=\"1\" CHECKED><br>" + . "Specify: <input type=\"radio\" name=\"st_none\" value=\"0\"> " + . "<select name=\"st_mon\" size=\"1\">"; + + for my $i (1 .. 12) { + if ($i == $cur_mon) { + print "<option selected value=\"$i\">$::d2m[$i]</option>\n"; + } + else { + print "<option value=\"$i\">$::d2m[$i]</option>\n"; + } + } + + print "</select>" + . "<select name=\"st_day\" size=\"1\">" + . "<option selected>1</option>\n"; + + for my $i (2 .. 31) { + print "<option value=\"$i\">$i</option>\n"; + } + + print "</select>" + . "<input type=\"text\" name=\"st_year\" size=\"6\" value=\"$cur_year\">\n"; + + # END DATE + print "<p>3. Enter the ending date:<br>\n" + . "None: <input type=\"radio\" name=\"end_none\" value=\"1\" CHECKED><br>\n" + . "Specify: <input type=\"radio\" name=\"end_none\" value=\"0\"> \n" + . "<select name=\"end_mon\" size=\"1\">\n"; + + for my $i (1 .. 12) { + if ($i == $cur_mon) { + print "<option selected value=\"$i\">$::d2m[$i]</option>\n"; + } + else { + print "<option value=\"$i\">$::d2m[$i]</option>\n"; + } + } + + print "</select>\n" + . "<select name=\"end_day\" size=\"1\">\n" + . "<option selected value=\"1\">1</option>\n"; + + for my $i (2 .. 31) { + print "<option value=\"$i\">$i</option>\n"; + } + + print "</select>" + . "<input type=\"text\" name=\"end_year\" size=\"6\" value=\"$cur_year\">\n"; + + # FILE NAME + print "<p>4. Enter the file name to save as:<br>" + . "<tt>$::DATADIR/</tt><input type=\"text\" size=36 name=\"fname\" value=\"timeline.txt\"><br>\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n"; + + # Get only the UNIX images - since only they have /etc/passwd and group + my @unix_imgs; + my $root_vol = ""; + foreach my $i (keys %Caseman::vol2ftype) { + my $f = $Caseman::vol2ftype{$i}; + + next + unless (($f =~ /^ext/) + || ($f =~ /^ufs/) + || ($f =~ /^linux/) + || ($f =~ /bsd$/) + || ($f =~ /^solaris$/) + || ($f =~ /^bsdi$/)); + + push @unix_vols, $i; + + # Keep a reference to an image with '/' as the mounting point + $root_vol = $i + if ($Caseman::vol2mnt{$i} eq '/'); + } + + my $cnt = 5; + if (scalar @unix_vols > 0) { + + print +"<p>$cnt. Select the UNIX image that contains the /etc/passwd and /etc/group files:<br>\n"; + $cnt++; + + print "<select name=\"pw_vol\">\n"; + + # If we did not find an image that has a / mounting point, then + # we will use none as the default. + if ($root_vol eq "") { + print "<option value=\"\" selected>None</option>\n"; + } + else { + print "<option value=\"\">None</option>\n"; + } + + foreach my $vol (@unix_vols) { + if ($root_vol eq $vol) { + print +"<option value=\"$vol\" selected>$Caseman::vol2sname{$vol} ($Caseman::vol2mnt{$vol})" + . "</option>\n"; + } + else { + print +"<option value=\"$vol\">$Caseman::vol2sname{$vol} ($Caseman::vol2mnt{$vol})</option>\n"; + } + } + + print "</select>\n"; + } + + print "<p>$cnt. Choose the output format:<br>\n"; + $cnt++; + print +" <input type=\"radio\" name=\"sort\" value=\"$OTYPE_NORM\" CHECKED>Tabulated (normal)<br>\n" + . " <input type=\"radio\" name=\"sort\" value=\"$OTYPE_HOURLY\">Comma delimited with hourly summary<br>\n" + . " <input type=\"radio\" name=\"sort\" value=\"$OTYPE_DAILY\">Comma delimited with daily summary<br>\n"; + + print "<p>$cnt. Generate MD5 Value? "; + $cnt++; + + print "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED>\n"; + + # Create Button + print "<p><input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return 0; +} + +sub tl_run { + Args::check_fname(); + Args::check_body(); + Args::check_sort(); + + Print::print_html_header("Make Timeline"); + + my $body = Args::get_body(); + my $fname = Args::get_fname(); + my $fname_rel = Args::get_fname_rel(); + my $otype = Args::get_sort(); + + my $fname_mode = $Args::args{'fname_mode'}; + + if ((-e "$fname") && ($FNAME_MODE_INIT == $fname_mode)) { + print "File Already Exists: <tt>$fname_rel</tt><br>\n"; + + my $hidden = + "<input type=\"hidden\" name=\"body\" value=\"$Args::args{'body'}\">" + . Args::make_hidden(); + + $hidden .= + "<input type=\"hidden\" name=\"st_none\" " + . "value=\"$Args::args{'st_none'}\">\n" + if (exists $Args::args{'st_none'}); + $hidden .= + "<input type=\"hidden\" name=\"st_year\" " + . "value=\"$Args::args{'st_year'}\">\n" + if (exists $Args::args{'st_year'}); + $hidden .= + "<input type=\"hidden\" name=\"st_day\" " + . "value=\"$Args::args{'st_day'}\">\n" + if (exists $Args::args{'st_day'}); + $hidden .= + "<input type=\"hidden\" name=\"st_mon\" " + . "value=\"$Args::args{'st_mon'}\">\n" + if (exists $Args::args{'st_mon'}); + $hidden .= + "<input type=\"hidden\" name=\"end_none\" " + . "value=\"$Args::args{'end_none'}\">\n" + if (exists $Args::args{'end_none'}); + $hidden .= + "<input type=\"hidden\" name=\"end_year\" " + . "value=\"$Args::args{'end_year'}\">\n" + if (exists $Args::args{'end_year'}); + $hidden .= + "<input type=\"hidden\" name=\"end_day\" " + . "value=\"$Args::args{'end_day'}\">\n" + if (exists $Args::args{'end_day'}); + $hidden .= + "<input type=\"hidden\" name=\"end_mon\" " + . "value=\"$Args::args{'end_mon'}\">\n" + if (exists $Args::args{'end_mon'}); + $hidden .= + "<input type=\"hidden\" name=\"tz\" " + . "value=\"$Args::args{'tz'}\">\n" + if (exists $Args::args{'tz'}); + $hidden .= + "<input type=\"hidden\" name=\"pw_vol\" " + . "value=\"$Args::args{'pw_vol'}\">\n" + if (exists $Args::args{'pw_vol'}); + $hidden .= + "<input type=\"hidden\" name=\"md5\" " + . "value=\"$Args::args{'md5'}\">\n" + if (exists $Args::args{'md5'}); + $hidden .= + "<input type=\"hidden\" name=\"sort\" " + . "value=\"$Args::args{'sort'}\">\n" + if (exists $Args::args{'sort'}); + + # Make a new name + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "New Name: <input type=\"text\" name=\"fname\">" + . "<table cellspacing=\"30\" cellpadding=\"2\"><tr><td>" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n" + . "$hidden\n" + . "<input type=\"image\" src=\"pict/but_new_name.jpg\" " + . "width=79 height=20 alt=\"Use New Name\" border=\"0\">\n" + . "</form></td>\n"; + + # Overwrite it + print "<td><form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"image\" src=\"pict/but_replace.jpg\" " + . "width=66 height=20 alt=\"Replace\" border=\"0\"><br>\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n" + . "<input type=\"hidden\" name=\"fname\" value=\"$Args::args{'fname'}\">\n" + . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_OVER\" +>\n" . "$hidden\n" . "</form></td></tr></table>"; + + return 0; + } + + my $mon; + my $day; + my $year; + + my $date = ""; + + # Get the start date + unless ((exists $Args::args{'st_none'}) && ($Args::args{'st_none'} == 1)) { + + if (exists $Args::args{'st_mon'}) { + Args::check_st_mon(); + $mon = Args::get_st_mon(); + if (($mon < 1) || ($mon > 12)) { + print("Invalid starting month\n"); + return 1; + } + if ($mon < 10) { + $mon = "0" . $mon; + } + } + if (exists $Args::args{'st_year'}) { + Args::check_st_year(); + $year = Args::get_st_year(); + if (($year < 1970) || ($year > 2020)) { + print("Invalid starting year\n"); + return 1; + } + } + if ( (exists $Args::args{'st_day'}) + && ($Args::args{'st_day'} =~ /^(\d\d?)$/)) + { + $day = $1; + if (($day < 1) || ($day > 31)) { + print("Invalid starting day\n"); + return 1; + } + if ($day < 10) { + $day = "0" . $day; + } + } + else { + print("Invalid start day\n"); + return 1; + } + + $date = "$year-$mon-$day"; + } + + unless ((exists $Args::args{'end_none'}) && ($Args::args{'end_none'} == 1)) + { + + if ($date eq "") { + print "Begin date must be given if ending date is given<br>"; + return 1; + } + + if ( (exists $Args::args{'end_mon'}) + && ($Args::args{'end_mon'} =~ /^(\d\d?)$/)) + { + $mon = $1; + if (($mon < 1) || ($mon > 12)) { + print("Invalid end month\n"); + return 1; + } + if ($mon < 10) { + $mon = "0" . $mon; + } + } + else { + print("Invalid end month\n"); + return 1; + } + if ( (exists $Args::args{'end_year'}) + && ($Args::args{'end_year'} =~ /^(\d\d\d\d)$/)) + { + $year = $1; + if (($year < 1970) || ($year > 2020)) { + print("Invalid ending year\n"); + return 1; + } + + } + else { + print("Invalid end year\n"); + return 1; + } + if ( (exists $Args::args{'end_day'}) + && ($Args::args{'end_day'} =~ /^(\d\d?)$/)) + { + $day = $1; + if (($day < 1) || ($day > 31)) { + print("Invalid end day\n"); + return 1; + } + if ($day < 10) { + $day = "0" . $day; + } + } + else { + print("Invalid end day\n"); + return 1; + } + + $date .= "..$year-$mon-$day"; + } + + # temp strings for the password and group files + my $pw_tmp = ""; + my $gr_tmp = ""; + my $mac_args = ""; + my $log = ""; + + local *OUT; + + # Password and Group Files + if ((exists $Args::args{'pw_vol'}) && ($Args::args{'pw_vol'} ne "")) { + Args::check_vol('pw_vol'); + my $pw_vol = Args::get_vol('pw_vol'); + + my $ftype = $Caseman::vol2ftype{$pw_vol}; + my $img = $Caseman::vol2path{$pw_vol}; + my $offset = $Caseman::vol2start{$pw_vol}; + my $imgtype = $Caseman::vol2itype{$pw_vol}; + + $log .= "Password & Group File ($pw_vol) "; + + # Get the passwd file meta and copy the file + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ifind' -f $ftype -n 'etc/passwd' -o $offset -i $imgtype $img" + ); + my $pwi = Exec::read_pipe_line(*OUT); + close(OUT); + + $pwi = "Error getting meta for passwd" + if ((!defined $pwi) || ($pwi eq "")); + + # Do the Taint Checking + if ($pwi =~ /^($::REG_META)$/) { + $pwi = $1; + + $log .= "Password Meta Address ($pwi) "; + + # Find a temp name that we can call it + my $i; + for ($i = 0;; $i++) { + unless (-e "$fname.pw-$i") { + $pw_tmp = "$fname.pw-$i"; + last; + } + } + + Exec::exec_sys( +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $pwi > '$pw_tmp'" + ); + $mac_args .= " -p \'$pw_tmp\' "; + + } + else { + print( +"Error finding /etc/passwd meta in $Caseman::vol2sname{$pw_vol} ($pwi)<br>" + ); + Print::log_host_inv( +"$Caseman::vol2sname{$pw_vol}: /etc/passwd file not found for timeline" + ); + } + + # Get the group file meta and copy the file + Exec::exec_pipe(*OUT, +"'$::TSKDIR/ifind' -f $ftype -n 'etc/group' -o $offset -i $imgtype $img" + ); + my $gri = Exec::read_pipe_line(*OUT); + close(OUT); + + $gri = "Error getting meta for group" + if ((!defined $gri) || ($gri eq "")); + + # Do the Taint Checking + if ($gri =~ /^($::REG_META)$/) { + $gri = $1; + + $log .= "Group Meta Address ($gri) "; + + # Find a temp name that we can call it + my $i; + for ($i = 0;; $i++) { + unless (-e "$fname.gr-$i") { + $gr_tmp = "$fname.gr-$i"; + last; + } + } + Exec::exec_sys( +"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $gri > '$gr_tmp'" + ); + $mac_args .= " -g \'$gr_tmp\' "; + } + else { + print( +"Error finding /etc/group meta in $Caseman::vol2sname{$pw_vol} ($gri)<br>" + ); + Print::log_host_inv( +"$Caseman::vol2sname{$pw_vol}: /etc/group file not found for timeline" + ); + } + } + + if ($date eq "") { + print + "Creating Timeline using all dates (Time Zone: $Caseman::tz)<br>\n"; + Print::log_host_inv( +"$Caseman::vol2sname{$body}: Creating timeline using all dates (TZ: $Caseman::tz) ${log}to $fname_rel" + ); + } + else { + print "Creating Timeline for $date (Time Zone: $Caseman::tz)<br>\n"; + Print::log_host_inv( +"$Caseman::vol2sname{$body}: Creating timeline for $date (TZ: $Caseman::tz) ${log}to $fname_rel" + ); + } + + my $tz = ""; + $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq ""); + + # mactime needs the path to run the 'date' command + $ENV{PATH} = "/bin:/usr/bin"; + local *OUT; + if ($otype == $OTYPE_NORM) { + Exec::exec_pipe(*OUT, +"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz -i day '${fname}.sum' $mac_args $date > '$fname'" + ); + } + elsif ($otype == $OTYPE_HOURLY) { + Exec::exec_pipe(*OUT, +"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz -d -i hour '${fname}.sum' $mac_args $date > '$fname'" + ); + } + elsif ($otype == $OTYPE_DAILY) { + Exec::exec_pipe(*OUT, +"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz -d -i day '${fname}.sum' $mac_args $date > '$fname'" + ); + } + else { + Print::print_err("Unknown output type"); + } + + print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT)); + close(OUT); + $ENV{PATH} = ""; + + # Remove the password and group files + unlink("$pw_tmp") if ($pw_tmp ne ""); + unlink("$gr_tmp") if ($gr_tmp ne ""); + + print "<br>Timeline saved to <tt>$fname</tt><br><br>\n"; + + # append to fsmorgue if a normal timeline + if ($otype == $OTYPE_NORM) { + my $tl_vol = Caseman::add_vol_host_config("timeline", $fname_rel); + print "Entry added to host config file<br><br>\n"; + + $Caseman::vol2cat{$tl_vol} = "timeline"; + $Caseman::vol2ftype{$tl_vol} = "timeline"; + $Caseman::vol2itype{$tl_vol} = "raw"; + $Caseman::vol2path{$tl_vol} = "$fname"; + $Caseman::vol2start{$tl_vol} = 0; + $Caseman::vol2end{$tl_vol} = 0; + $Caseman::vol2sname{$tl_vol} = $fname_rel; + + # Calculate MD5 + if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) { + print "Calculating MD5 Value<br><br>\n"; + my $m = Hash::int_create_wrap($tl_vol); + print "MD5 Value: <tt>$m</tt><br><br>\n"; + } + + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n" + . "<input type=\"hidden\" name=\"tl\" value=\"$tl_vol\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::FRAME\">\n" + . "<input type=\"hidden\" name=\"submod\" value=\"$Timeline::VIEW_FR\">\n" + . Args::make_hidden() + . "<input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n" + . "(NOTE: It is easier to view the timeline in a text editor than here)"; + } + else { + print + "Comma delimited files cannot be viewed from within Autopsy.<br>\n" + . "Open it in a spreadsheet or other data processing tool.<br>\n"; + } + Print::print_html_footer(); + return 0; +} + +sub view_menu { + Print::print_html_header("View Timeline Menu"); + + my @tl; + + # Find the timelines in the images hash + foreach my $k (keys %Caseman::vol2cat) { + if ( ($Caseman::vol2cat{$k} eq "timeline") + && ($Caseman::vol2ftype{$k} eq "timeline")) + { + push @tl, $k; + } + } + + if (scalar(@tl) == 0) { + print "There are currently no timeline files in the " + . "host config file.<br>One must first be created before you " + . "can view it<br>\n"; + + print "<p><a target=\"_top\" " + . "href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&view=$Timeline::FRAME&" + . "submod=$Timeline::TL_ENTER\">" + . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=\"43\" height=20 border=\"0\">" + . "</a>\n"; + + return 1; + } + + print "<form action=\"$::PROGNAME\" method=\"get\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::VIEW_FR\">\n" + . Args::make_hidden() + . "1. Select the timeline file:\n" + . "<table cellspacing=\"0\" cellpadding=\"2\">\n"; + + my @tl_sort = sort { lc($a) cmp lc($b) } @tl; + for (my $i = 0; $i <= $#tl_sort; $i++) { + print "<tr><td><input type=\"radio\" name=\"tl\" " + . "value=\"$tl_sort[$i]\"></td><td>$Caseman::vol2sname{$tl_sort[$i]}</td>\n"; + } + + print "</table>\n" + . "<input type=\"image\" src=\"pict/but_ok.jpg\" " + . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"; + + Print::print_html_footer(); + return 0; +} + +sub view_fr { + Args::check_tl(); + + Print::print_html_header_frameset(""); + my $tl_vol = Args::get_tl(); + my $tl = $Caseman::vol2path{$tl_vol}; + my $url = ""; + + unless (exists $Args::args{'st_mon'}) { + + unless (open(TL, $tl)) { + print("Error opening $tl"); + return (1); + } + + my $beg_mon; + my $beg_year; + my $cnt = 0; + while (<TL>) { + $cnt++; + if (/^(?:\w\w\w )?(\w\w\w)\s+\d\d\s+(\d\d\d\d)\s+\d\d:\d\d:\d\d/) { + $url = "tl=$tl_vol&st_mon=$::m2d{$1}&st_year=$2"; + + } + last; + } + close(TL); + + if ($cnt == 0) { + print "Empty timeline<br>\n"; + return 1; + } + if ($url eq "") { + print "Invalid Timeline<br>\n"; + return 1; + } + } + else { + $url = + "tl=$tl_vol&st_mon=$Args::enc_args{'st_mon'}&" + . "st_year=$Args::enc_args{'st_year'}"; + } + + print "<frameset rows=\"65,*\">\n" + . "<frame src=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&" + . "view=$Timeline::VIEW_IDX&$url\">\n" + . "<frame src=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&" + . "view=$Timeline::VIEW&$url\">\n</frameset>"; + + Print::print_html_footer(); + return 0; +} + +sub view_idx { + Args::check_st_mon(); + Args::check_st_year(); + Args::check_tl(); + + Print::print_html_header("View Timeline Index"); + + my $mon = Args::get_st_mon(); + my $year = Args::get_st_year(); + my $tl_vol = Args::get_tl(); + my $tl = $Caseman::vol2path{$tl_vol}; + + print "<center>"; + my $url = + "$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&view=$Timeline::VIEW_FR&" + . "tl=$tl_vol"; + + # Next and Previous pointers + my $pyear = $year; + my $pmon = $mon - 1; + if ($pmon == 0) { + $pmon = 12; + $pyear--; + } + print "<table cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr><td align=\"center\">" + . "<a href=\"$url&st_mon=$pmon&st_year=$pyear\" target=\"_parent\">" + . "<- $::d2m[$pmon] $pyear</a></td>\n" + . "<td> </td>\n"; + + if (-e "${tl}.sum") { + print "<td><a href=\"$::PROGNAME?$Args::baseargs&" + . "mod=$::MOD_TL&view=$Timeline::VIEW_SUM&" + . "tl=$tl_vol\" target=\"_parent\">" + . "Summary</td>\n" + . "<td> </td>\n"; + } + + my $nyear = $year; + my $nmon = $mon + 1; + if ($nmon == 13) { + $nmon = 1; + $nyear++; + } + + print "<td align=\"center\">" + . "<a href=\"$url&st_mon=$nmon&st_year=$nyear\" target=\"_parent\">" + . "$::d2m[$nmon] $nyear -></a></td></tr></table>\n"; + + # Make a form to enter the next month and year to show. + # it defaults to the current location + print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_parent\">\n" + . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n" + . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::VIEW_FR\">\n" + . "<input type=\"hidden\" name=\"tl\" value=\"$tl_vol\">\n" + . Args::make_hidden() + . + + "<table cellspacing=\"0\" cellpadding=\"2\">\n" + . "<tr><td align=\"center\"><select name=\"st_mon\" size=\"1\">\n"; + + for my $i (1 .. 12) { + if ($i == $mon) { + print "<option selected value=\"$i\">$::d2m[$i]</option>\n"; + } + else { + print "<option value=\"$i\">$::d2m[$i]</option>\n"; + } + } + + print "</select></td>" + . "<td align=\"center\">" + . "<input type=\"text\" name=\"st_year\" size=\"6\" value=\"$year\">" + . "</td>" + . "<td align=\"center\">" + . "<input type=\"image\" src=\"pict/but_ok.jpg\" alt=\"Ok\" " + . "width=43 height=20 border=\"0\"></td>\n" + . "</tr></table></form>\n"; + + Print::print_html_footer(); + return 0; +} + +# Display the contents of the summary file (hits per day) and show +# it as hits per month +sub view_sum { + Args::check_tl(); + + Print::print_html_header("View Timeline Summary"); + + my $tl_vol = Args::get_tl(); + my $tl = $Caseman::vol2path{$tl_vol}; + + $tl .= ".sum"; + + open(TL, "<$tl") or die "Can not open $tl"; + my $url = + "$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&" + . "view=$Timeline::VIEW_FR&tl=$tl_vol"; + + print "<p>This page provides a monthly summary of activity.<br>" + . "Each day that has activity is noted with the number of events<br>\n"; + + my $p_year = ""; + my $p_mon = ""; + + print "<p><table cellspacing=2 border=0>\n"; + + while (<TL>) { + my @a = split(/ /, $_); + next unless (scalar(@a) == 5); + my $mon = $::m2d{$a[1]}; + my $year = $a[3]; + $year = $1 if ($year =~ /^(\d{4,4}):$/); + + if (($p_year ne $year) || ($p_mon ne $mon)) { + print "<tr><td colspan=6 align=left>" + . "<a href=\"$url&st_mon=$mon&st_year=$year\">" + . "$a[1] $year</a></td></tr>\n"; + + $p_year = $year; + $p_mon = $mon; + } + print "<tr><td> </td><td>$a[0]</td>" + . "<td>$a[1]</td><td>$a[2]</td><td>$year</td><td>($a[4])</td></tr>\n"; + } + print "</table>\n"; + + close(TL); + + Print::print_html_footer(); + return 0; +} + +# display a given month of the timeline +sub view { + Args::check_tl(); + Args::check_st_mon(); + Args::check_st_year(); + + my $tl_vol = Args::get_tl(); + my $tl = $Caseman::vol2path{$tl_vol}; + my $st_mon = Args::get_st_mon(); + my $st_year = Args::get_st_year(); + + Print::print_html_header("View $st_mon, $st_year of Timeline"); + + unless (open(TL, "$tl")) { + print("Error opening $tl"); + return (1); + } + + Print::log_host_inv( + "$Args::args{'tl'}: Viewing timeline for $::d2m[$st_mon] $st_year"); + + print "<table cellspacing=\"5\" cellpadding=\"2\" width=100%>\n"; + + # zone identifies if we should be printing or not + my $zone = 0; + my $row = 0; + while (<TL>) { + if ( +/^(?:(\w\w\w\s+)?(\w\w\w\s+\d\d\s+\d\d\d\d)\s+(\d\d:\d\d:\d\d))?\s+(\d+)\s+([macb\.]+)\s+([-\/\?\w]+)\s+([\d\w\/]+)\s+([\d\w\/]+)\s+($::REG_META)\s+(.*)$/o + ) + { + + my $day = ""; + $day = $1 if (defined $1); + my $date = ""; + $date = $2 if (defined $2); + my $time = ""; + $time = $3 if (defined $3); + my $sz = $4; + my $mac = $5; + my $p = $6; + my $u = $7; + my $g = $8; + my $i = $9; + my $f = $10; + + # we must break this down to see if we can skip it or not + if ($date ne "") { + if ($date =~ /^(\w\w\w)\s+\d\d\s+(\d\d\d\d)$/) { + if ($2 < $st_year) { + next; + } + elsif (($2 == $st_year) && ($::m2d{$1} < $st_mon)) { + next; + } + elsif ($2 > $st_year) { + last; + } + elsif (($2 == $st_year) && ($::m2d{$1} > $st_mon)) { + last; + } + else { + $zone = 1; + } + } + } + + # we need to print this entry + if ($zone) { + + # the deleted meta <blah-dead-2> entries screw up in HTML + $f = "<$1 >" if ($f =~ /^<(.*?)>$/); + + if (($row % 2) == 0) { + print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR\">\n"; + } + else { + print + "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR_TABLE\">\n"; + } + + print "<td>$day $date $time</td>" + . "<td>$sz</td><td>$mac</td><td>$p</td>" + . "<td>$u</td><td>$g</td><td>$i</td><td>" + . Print::html_encode($f) + . "</td></tr>\n"; + + $row++; + } + } + else { + print "Error parsing timeline: " + . Print::html_encode($_) + . "<br>\n"; + } + } + close(TL); + print "</table>"; + + Print::print_html_footer(); + return 0; +} + +# Blank Page +sub blank { + Print::print_html_header(""); + print "<center><h3>File Activity Timelines</h3>\n" + . "Here you can create a timeline of file activity.<br>\n" + . "This process requires two steps:<p>\n" + . "1. <b>Create Data File</b> from file system data ->" + . " 2. <b>Create Timeline</b> from the data file\n" + . "<p>Use the tabs above to start.\n"; + Print::print_html_footer(); + return 0; +} diff --git a/lib/Vs.pm b/lib/Vs.pm new file mode 100644 index 0000000000000000000000000000000000000000..2ce43f3fb0b5407f8835f63a2c7c906871ece200 --- /dev/null +++ b/lib/Vs.pm @@ -0,0 +1,8 @@ +package Vs; + +# These need to be updated as The Sleuth Kit supports more volume systems +$Vs::type{'dos'} = 1; +$Vs::type{'bsd'} = 1; +$Vs::type{'gpt'} = 1; +$Vs::type{'mac'} = 1; +$Vs::type{'sun'} = 1; diff --git a/lib/define.pl b/lib/define.pl new file mode 100644 index 0000000000000000000000000000000000000000..9bce1fa80507702ff2e9f6fbdb280e8e4e3fb478 --- /dev/null +++ b/lib/define.pl @@ -0,0 +1,39 @@ +# +$VER = '2.24'; + +$HTTP_NL = "\x0a"; +$notes_file = ""; + +$PICTDIR = "$INSTALLDIR/pict/"; +$SANITIZE_TAG = 'AutopsySanitized'; +$SANITIZE_PICT = 'sanitized.jpg'; + +$PROGNAME = 'autopsy'; + +# Default directory names +$MKDIR_MASK = 0775; +$IMGDIR = 'images'; +$DATADIR = 'output'; +$LOGDIR = 'logs'; +$REPDIR = 'reports'; + +# Colors +$BACK_COLOR = "#CCCC99"; +$BACK_COLOR_TABLE = "#CCCCCC"; +$DEL_COLOR[0] = "red"; +$DEL_COLOR[1] = "#800000"; # used when meta data structure has been reallocated +$NORM_COLOR = ""; +$LINK_COLOR = "blue"; + +$YEL_PIX = "pict/back_pix.jpg"; + +%m2d = ( + "Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6, + "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12 +); +@d2m = ( + "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" +); + +1; diff --git a/lib/search.pl b/lib/search.pl new file mode 100644 index 0000000000000000000000000000000000000000..4ea1b363d1a72de1e2377d4f535302569ceb08f8 --- /dev/null +++ b/lib/search.pl @@ -0,0 +1,51 @@ +# +# This file contains pre-defined search strings. A button for each will +# be displayed in the Search Mode. +# +# The $auto_srch{} hash is filled in with the search string +# The index into the hash is the name of the search. +# +# For example, $auto_srch{'foo'} = "bar"; would search for the string +# bar +# +# If the search is case sensitive, then set $auto_srch_csense to 1 (this +# is the default value if not specified. Set to 0 for insensitive +# +# If the search is a regular expression, set $auto_srch_reg to 1, else +# 0 (the default) +# +# +# If you develop patterns that you think will be useful to others, email +# them to me and I will include them in the next version (carrier@sleuthkit.org) +# + +# Date / syslog search of month and date +$auto_srch{'Date'} = +"((jan)|(feb)|(mar)|(apr)|(may)|(june?)|(july?)|(aug)|(sept?)|(oct)|(nov)|(dec))([[:space:]]+[[:digit:]])?"; +$auto_srch_reg{'Date'} = 1; +$auto_srch_csense{'Date'} = 0; + +# IP Address +$auto_srch{'IP'} = +'[0-2]?[[:digit:]]{1,2}\.[0-2]?[[:digit:]]{1,2}\.[0-2]?[[:digit:]]{1,2}\.[0-2]?[[:digit:]]{1,2}'; +$auto_srch_reg{'IP'} = 1; +$auto_srch_csense{'IP'} = 0; + +# SSN in the pattern of 123-12-1234 - from Jerry Shenk +$auto_srch{'SSN1'} = '[0-9][0-9][0-9]\-[0-9]]0-9]\-[0-9][0-9][0-9][0-9]'; +$auto_srch_reg{'SSN1'} = 1; +$auto_srch_csense{'SSN1'} = 0; + +# SSN in the pattern of 123121234 - from Jerry Shenk +$auto_srch{'SSN2'} = '[0-9][0-9][0-9][0-9]]0-9][0-9][0-9][0-9][0-9]'; +$auto_srch_reg{'SSN2'} = 1; +$auto_srch_csense{'SSN2'} = 0; + +# CC # - from Jerry Shenk +$auto_srch{'CC'} = + '[0-9][0-9][0-9][0-9]]0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]]0-9]'; +$auto_srch_reg{'CC'} = 1; +$auto_srch_csense{'CC'} = 0; + +# This must be the last value +1; diff --git a/man/man1/autopsy.1 b/man/man1/autopsy.1 new file mode 100755 index 0000000000000000000000000000000000000000..39f983d6a7e7367c1e09b0e4483d8dd18fc8a7ff --- /dev/null +++ b/man/man1/autopsy.1 @@ -0,0 +1,163 @@ +.TH AUTOPSY 1 "MAR 2005" "User Manuals" +.SH NAME +autopsy \- Autopsy Forensic Browser +.SH SYNOPSIS +.B autopsy [-c] [-C] [-d +.I evid_locker +.B ] [-i +device filesystem mnt +.B ] [-p +.I port +.B ] +.I [addr] +.SH DESCRIPTION +By default, +.B autopsy +starts the Autopsy Forensic Browser server on port 9999 and and accepts +connections from the localhost. If +.I -p port +is given, then the server opens on that port and if +.I addr +is given, then connections are only accepted from that host. +When the +.I -i +argument is given, then autopsy goes into live analysis mode. + +The arguments are as follows: +.IP "-c" +Force the program to use cookies even for localhost. +.IP "-C" +Force the program to not use cookies even for remote hosts. +.IP "-d evid_locker" +Directory where cases and hosts are stored. +This overrides the +.B LOCKDIR +value in +.I conf.pl. +The path must be a full path (i.e. start with /). +.IP "-i device filesystem mnt" +Specify the information for the live analysis mode. This can be specified +as many times as needed. The +.I device +field is for the raw file system device, the +.I filesystem +field is for the file system type, and the +.I mnt +field is for the mounting point of the file system. +.IP "-p port" +TCP port for server to listen on. +.IP addr +IP address or host name of where investigator is located. +If localhost is used, then 'localhost' must be used in the URL. +If you use the actual hostname or IP, it will be rejected. +.PP +When started, the program will display a URL to paste into an +HTML browser. The browser must support frames and forms. The +Autopsy Forensic Browser will allow an investigator to analyze +images generated by +.BR dd(1) +for evidence. The program allows the images to be analyzed by +browsing files, blocks, inodes, or by searching the blocks. +The program also generates Autopsy reports that include collection +time, investigators name, and MD5 hash values. +.SH VARIABLES +The following variables can be set in +.I conf.pl. + +.I USE_STIMEOUT +.RS +When set to 1 (default is 0), the server will exit after +.B STIMEOUT +seconds of inactivity (default is 3600). This setting is recommended if +cookies are not used. +.RE +.I BASEDIR +.RS +Directory where cases and forensic images are located. +The images must have simple +names with only letters, numbers, '_', '-', and '.'. (See FILES). +.RE +.I TSKDIR +.RS +Directory where The Sleuth Kit binaries are located. +.RE +.I NSRLDB +.RS +Location of the NIST National Software Reference Library (NSRL). +.RE +.I INSTALLDIR +.RS +Directory where Autopsy was installed. +.RE +.I GREP_EXE +.RS +Location of +.BR grep(1) +binary. +.RE +.I STRINGS_EXE +.RS +Location of +.BR strings(1) +binary. +.RE +.SH FILES +.I Evidence Locker +.RS +The Evidence Locker is where all cases and hosts will be saved to. It +is a directory that will have a directory for each case. Each case +directory will have a directory for each host. + +.RE +.I <CASE_DIR>/case.aut +.RS +This file is the case configuration file for the case. It contains the +description of the case and default subdirectories for the hosts. + +.RE +.I <CASE_DIR>/investigators.txt +.RS +This file contains the list of investigators that will use this case. These +are used for logging only, not authentication. + +.RE +.I <HOST_DIR>/host.aut +.RS +This file is where the host configuration details are saved. It +is similar to the 'fsmorgue' file from previous versions of Autopsy. +It has an entry for each file in the host and contains the host +description. + + +.RE +.I md5.txt +.RS +Some directories will have this file in it. It contains MD5 values for +important files in the directory. This makes it easy to validate the +integrity of images. + +.SH EXAMPLE +# ./autopsy -p 8888 10.1.34.19 +.SH "SEE ALSO" +.BR dd (1), +.BR fls (1), +.BR ffind (1), +.BR ifind (1), +.BR grep (1), +.BR icat (1) +.BR md5 (1), +.BR strings (1), +.SH REQUIREMENTS +The Autopsy Forensic Browser requires +.B The Sleuth Kit +<www.sleuthkit.org/sleuthkit> + +.SH HISTORY +.BR "autopsy" " first appeared in " "Autopsy" " v1.0." +.SH LICENSE +This software is distributed under the GNU Public License. + +.SH AUTHOR +Brian Carrier <carrier at sleuthkit dot org> + +Send documentation updates to <doc-updates at sleuthkit dot org> diff --git a/pict/back_pix.jpg b/pict/back_pix.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1a5da64c9bda1ad2cec4418a7c7b055b97e2f7da Binary files /dev/null and b/pict/back_pix.jpg differ diff --git a/pict/but_addnote.jpg b/pict/but_addnote.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0946f296f4a91ab4ea595689ccd02031f38c1b15 Binary files /dev/null and b/pict/but_addnote.jpg differ diff --git a/pict/but_alloc_list.jpg b/pict/but_alloc_list.jpg new file mode 100644 index 0000000000000000000000000000000000000000..525fd081f3ff28dd75136354110695c71f43986d Binary files /dev/null and b/pict/but_alloc_list.jpg differ diff --git a/pict/but_export.jpg b/pict/but_export.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4cbc75a024be2c929a5f8acdad048169b7d2573c Binary files /dev/null and b/pict/but_export.jpg differ diff --git a/pict/but_force.jpg b/pict/but_force.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a92475c71feecd87e414c36c993dba5eaea0966 Binary files /dev/null and b/pict/but_force.jpg differ diff --git a/pict/but_indexdb.jpg b/pict/but_indexdb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5e51b780546e75ec6d474bdeec4f29dd7e5b6b47 Binary files /dev/null and b/pict/but_indexdb.jpg differ diff --git a/pict/but_lookup.jpg b/pict/but_lookup.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b6bd6daf9fb7701f56d040a09e485e7246a13deb Binary files /dev/null and b/pict/but_lookup.jpg differ diff --git a/pict/but_new_name.jpg b/pict/but_new_name.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a24bb42ea857421da071bb977e95469751b65a2 Binary files /dev/null and b/pict/but_new_name.jpg differ diff --git a/pict/but_next.jpg b/pict/but_next.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c5639baeb50091ab6043b3d8a97d66125f3f8468 Binary files /dev/null and b/pict/but_next.jpg differ diff --git a/pict/but_ok.jpg b/pict/but_ok.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bb2996fa2f3ccd958b8f3ccdbf788406799b6ef Binary files /dev/null and b/pict/but_ok.jpg differ diff --git a/pict/but_prev.jpg b/pict/but_prev.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46c4f397005d169866102814cac08802f19acc4c Binary files /dev/null and b/pict/but_prev.jpg differ diff --git a/pict/but_replace.jpg b/pict/but_replace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c17557a59ec5367fe5e1970346c8b71060b3fcd Binary files /dev/null and b/pict/but_replace.jpg differ diff --git a/pict/but_report.jpg b/pict/but_report.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e283c36212ec407630b1956482b898960f305bf3 Binary files /dev/null and b/pict/but_report.jpg differ diff --git a/pict/but_search.jpg b/pict/but_search.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ed5287e349e2f92564d49534908f4bdde0799155 Binary files /dev/null and b/pict/but_search.jpg differ diff --git a/pict/but_view.jpg b/pict/but_view.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90a0ae9b60482d54a3956a2f8fd60039dadcfd3a Binary files /dev/null and b/pict/but_view.jpg differ diff --git a/pict/but_viewcont.jpg b/pict/but_viewcont.jpg new file mode 100644 index 0000000000000000000000000000000000000000..76f58842eee5c730cac18badbefdcf86db88006a Binary files /dev/null and b/pict/but_viewcont.jpg differ diff --git a/pict/favicon.ico b/pict/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0a4ba44527995af4c14f35423c64ff52c518e87c Binary files /dev/null and b/pict/favicon.ico differ diff --git a/pict/file_b_alldel.jpg b/pict/file_b_alldel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e82f225dddcf276465c90e7be790bbeddf72efea Binary files /dev/null and b/pict/file_b_alldel.jpg differ diff --git a/pict/file_b_allfiles.jpg b/pict/file_b_allfiles.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac88e0c2711fa690c927cca15e5fd590018bca98 Binary files /dev/null and b/pict/file_b_allfiles.jpg differ diff --git a/pict/file_b_check.jpg b/pict/file_b_check.jpg new file mode 100644 index 0000000000000000000000000000000000000000..68049e674a4b13538ad1e65aa330177c31571ef3 Binary files /dev/null and b/pict/file_b_check.jpg differ diff --git a/pict/file_b_expand.jpg b/pict/file_b_expand.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86b720efa5126dae8d836e156b9cfa4f92a87626 Binary files /dev/null and b/pict/file_b_expand.jpg differ diff --git a/pict/file_b_hide.jpg b/pict/file_b_hide.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5689c05b18cb36ce96a771a697cde40c99d137b7 Binary files /dev/null and b/pict/file_b_hide.jpg differ diff --git a/pict/file_b_md5list.jpg b/pict/file_b_md5list.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9dac25518acf9deaa84115d9156f7e9b62f0dc34 Binary files /dev/null and b/pict/file_b_md5list.jpg differ diff --git a/pict/file_h_acc_cur.jpg b/pict/file_h_acc_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8907e17b7b98daaf1e579501ba8d72243b0a4888 Binary files /dev/null and b/pict/file_h_acc_cur.jpg differ diff --git a/pict/file_h_acc_link.jpg b/pict/file_h_acc_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..50d9ca2a3289308e6919988c79a3095015aa6342 Binary files /dev/null and b/pict/file_h_acc_link.jpg differ diff --git a/pict/file_h_chg_cur.jpg b/pict/file_h_chg_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f987ce883e1a3c8714efb32b5eaf8452c551064 Binary files /dev/null and b/pict/file_h_chg_cur.jpg differ diff --git a/pict/file_h_chg_link.jpg b/pict/file_h_chg_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8dab273e0c039b89bd1c208f1f0890337d4913ec Binary files /dev/null and b/pict/file_h_chg_link.jpg differ diff --git a/pict/file_h_cre_cur.jpg b/pict/file_h_cre_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0df3fc9a0e82d51b3fe13e458784b6fe6a05471a Binary files /dev/null and b/pict/file_h_cre_cur.jpg differ diff --git a/pict/file_h_cre_link.jpg b/pict/file_h_cre_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3861f661ef4ae1cb86f947ee51cf0107434f26d0 Binary files /dev/null and b/pict/file_h_cre_link.jpg differ diff --git a/pict/file_h_del_cur.jpg b/pict/file_h_del_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..38a713b9ab044ac68e3479d4a1acbeef7d355cf5 Binary files /dev/null and b/pict/file_h_del_cur.jpg differ diff --git a/pict/file_h_del_link.jpg b/pict/file_h_del_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..253db16e8c571653a3bf19303e99d324defedf21 Binary files /dev/null and b/pict/file_h_del_link.jpg differ diff --git a/pict/file_h_gid_cur.jpg b/pict/file_h_gid_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e775b344bdb256afb445770a8a9314b1a48a64f6 Binary files /dev/null and b/pict/file_h_gid_cur.jpg differ diff --git a/pict/file_h_gid_link.jpg b/pict/file_h_gid_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..38833628098e304ee60396383c984734bf865f40 Binary files /dev/null and b/pict/file_h_gid_link.jpg differ diff --git a/pict/file_h_meta_cur.jpg b/pict/file_h_meta_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..62d64d95d7831aaea0fad1291bb29737c1ea4f14 Binary files /dev/null and b/pict/file_h_meta_cur.jpg differ diff --git a/pict/file_h_meta_link.jpg b/pict/file_h_meta_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d691cfe2731222df8d204212276ac7cae98276a9 Binary files /dev/null and b/pict/file_h_meta_link.jpg differ diff --git a/pict/file_h_mod_cur.jpg b/pict/file_h_mod_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f08924e959bceefca05ed73511f9af94e2dd61b0 Binary files /dev/null and b/pict/file_h_mod_cur.jpg differ diff --git a/pict/file_h_mod_link.jpg b/pict/file_h_mod_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6e7aabfe996bb10897e621c3354b58efa16e47d Binary files /dev/null and b/pict/file_h_mod_link.jpg differ diff --git a/pict/file_h_nam_cur.jpg b/pict/file_h_nam_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19c3d43d6307aec2b3699edeec4a9e931e684b72 Binary files /dev/null and b/pict/file_h_nam_cur.jpg differ diff --git a/pict/file_h_nam_link.jpg b/pict/file_h_nam_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..76c9de71e733b887e1b59b6a37c53e08cb10ed3b Binary files /dev/null and b/pict/file_h_nam_link.jpg differ diff --git a/pict/file_h_siz_cur.jpg b/pict/file_h_siz_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..17b4fadaa92f3c5446bede9d16079305d0d9c400 Binary files /dev/null and b/pict/file_h_siz_cur.jpg differ diff --git a/pict/file_h_siz_link.jpg b/pict/file_h_siz_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27527d69a2d58581d11f25c57d1bc702cf13730d Binary files /dev/null and b/pict/file_h_siz_link.jpg differ diff --git a/pict/file_h_uid_cur.jpg b/pict/file_h_uid_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa1d27ea3187815a95bf7cbdf5203a6098d46cb2 Binary files /dev/null and b/pict/file_h_uid_cur.jpg differ diff --git a/pict/file_h_uid_link.jpg b/pict/file_h_uid_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..29bab9c18fab59b73c9f4d91010c91cc552aba61 Binary files /dev/null and b/pict/file_h_uid_link.jpg differ diff --git a/pict/file_h_wr_cur.jpg b/pict/file_h_wr_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd9d380ed717fe727c1f550d0058f1c2ea5631b4 Binary files /dev/null and b/pict/file_h_wr_cur.jpg differ diff --git a/pict/file_h_wr_link.jpg b/pict/file_h_wr_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..709726d663f642f0a364aaea76b3c3fe44b7e3c5 Binary files /dev/null and b/pict/file_h_wr_link.jpg differ diff --git a/pict/hashdb_h_alert.jpg b/pict/hashdb_h_alert.jpg new file mode 100644 index 0000000000000000000000000000000000000000..55301aacd7eadcded069585668d40a3474c405dc Binary files /dev/null and b/pict/hashdb_h_alert.jpg differ diff --git a/pict/hashdb_h_ig.jpg b/pict/hashdb_h_ig.jpg new file mode 100644 index 0000000000000000000000000000000000000000..edf80bbadc96f8b137c461a99b05dbf7beda2f34 Binary files /dev/null and b/pict/hashdb_h_ig.jpg differ diff --git a/pict/hashdb_h_nsrl.jpg b/pict/hashdb_h_nsrl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b333af90c19ac2f680246ead9725025d5702aba7 Binary files /dev/null and b/pict/hashdb_h_nsrl.jpg differ diff --git a/pict/int_b_calc.jpg b/pict/int_b_calc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..db33eca6ba0396288096a5e37e5b3e872402cb17 Binary files /dev/null and b/pict/int_b_calc.jpg differ diff --git a/pict/int_b_valid.jpg b/pict/int_b_valid.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aee869b6e65f18e68ede4ce08d69eafd8548e593 Binary files /dev/null and b/pict/int_b_valid.jpg differ diff --git a/pict/int_h_data.jpg b/pict/int_h_data.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cf69f3704e4de9f5ba945e4958b33a99463d0271 Binary files /dev/null and b/pict/int_h_data.jpg differ diff --git a/pict/int_h_img.jpg b/pict/int_h_img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a22cf45eee60064970f62bbb0512f933eedb798c Binary files /dev/null and b/pict/int_h_img.jpg differ diff --git a/pict/int_h_str.jpg b/pict/int_h_str.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8b3e8e75183f672994acad1851bcfe21059d957 Binary files /dev/null and b/pict/int_h_str.jpg differ diff --git a/pict/int_h_tl.jpg b/pict/int_h_tl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1afeed84e02ea11d23704a72ee1c23213c89cb5 Binary files /dev/null and b/pict/int_h_tl.jpg differ diff --git a/pict/int_h_unalloc.jpg b/pict/int_h_unalloc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce7fa58139f09bb12d104c9715a5232e7653aa1e Binary files /dev/null and b/pict/int_h_unalloc.jpg differ diff --git a/pict/logo.jpg b/pict/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0d9c29c3826757ccb30110bebf418ddc79121a3c Binary files /dev/null and b/pict/logo.jpg differ diff --git a/pict/main_t_dat_cur.jpg b/pict/main_t_dat_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..02bf5442e84a7dec1a0f90f16c75a33c9490666a Binary files /dev/null and b/pict/main_t_dat_cur.jpg differ diff --git a/pict/main_t_dat_link.jpg b/pict/main_t_dat_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e40990093dfef4c2c9591bc360abf67f1a7a91d0 Binary files /dev/null and b/pict/main_t_dat_link.jpg differ diff --git a/pict/main_t_fil_cur.jpg b/pict/main_t_fil_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..91a4a83e20501b946b05609807e09d7e5908d2be Binary files /dev/null and b/pict/main_t_fil_cur.jpg differ diff --git a/pict/main_t_fil_link.jpg b/pict/main_t_fil_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..82b814c7d6cca4dfbc31853f308104f9506c733c Binary files /dev/null and b/pict/main_t_fil_link.jpg differ diff --git a/pict/main_t_fil_org.jpg b/pict/main_t_fil_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..150bdb49620d650abb431975e7a90b6de2d89f4c Binary files /dev/null and b/pict/main_t_fil_org.jpg differ diff --git a/pict/main_t_ftype_cur.jpg b/pict/main_t_ftype_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c72918d7f8ec4f6cdc160c79efc6720535976adc Binary files /dev/null and b/pict/main_t_ftype_cur.jpg differ diff --git a/pict/main_t_ftype_link.jpg b/pict/main_t_ftype_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9161ec5fdc1ea495905da6173620490adf293c13 Binary files /dev/null and b/pict/main_t_ftype_link.jpg differ diff --git a/pict/main_t_ftype_org.jpg b/pict/main_t_ftype_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..801c1b075a92baeae0b4e1c39f1cd43d09c7ddc2 Binary files /dev/null and b/pict/main_t_ftype_org.jpg differ diff --git a/pict/main_t_img_cur.jpg b/pict/main_t_img_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5de656e4a65246e1acc8983fa96cc3ff01b100f4 Binary files /dev/null and b/pict/main_t_img_cur.jpg differ diff --git a/pict/main_t_img_link.jpg b/pict/main_t_img_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..51c1654dfb95cffed252a2ecfdb1937713f13c62 Binary files /dev/null and b/pict/main_t_img_link.jpg differ diff --git a/pict/main_t_img_org.jpg b/pict/main_t_img_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bdc0d2935524e5896b803c1a4bc4c788dc86f2d3 Binary files /dev/null and b/pict/main_t_img_org.jpg differ diff --git a/pict/main_t_met_cur.jpg b/pict/main_t_met_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..64016bdeca984d722a58c4321800da4a43b1907b Binary files /dev/null and b/pict/main_t_met_cur.jpg differ diff --git a/pict/main_t_met_link.jpg b/pict/main_t_met_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cfe4494153ffe652e09299998e4cab5dd2aadb87 Binary files /dev/null and b/pict/main_t_met_link.jpg differ diff --git a/pict/main_t_met_org.jpg b/pict/main_t_met_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5c014064d8e9c2bc1ced606167c1fa897eaecfa Binary files /dev/null and b/pict/main_t_met_org.jpg differ diff --git a/pict/main_t_srch_cur.jpg b/pict/main_t_srch_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9b99415c84b83ea6f473c103c520fa01cee888ae Binary files /dev/null and b/pict/main_t_srch_cur.jpg differ diff --git a/pict/main_t_srch_link.jpg b/pict/main_t_srch_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8087c4a48bb9a39e1b063f4bca601a0657ad0645 Binary files /dev/null and b/pict/main_t_srch_link.jpg differ diff --git a/pict/menu_b_add.jpg b/pict/menu_b_add.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9a50a3c0b18105b65b81bd13c62faf37aa0b9288 Binary files /dev/null and b/pict/menu_b_add.jpg differ diff --git a/pict/menu_b_analyze.jpg b/pict/menu_b_analyze.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab8cad740061be354bf0a27cae798a6e5ec391ef Binary files /dev/null and b/pict/menu_b_analyze.jpg differ diff --git a/pict/menu_b_back.jpg b/pict/menu_b_back.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d42433316343b73e8ca5aa4ee1b72df7e3ab1e19 Binary files /dev/null and b/pict/menu_b_back.jpg differ diff --git a/pict/menu_b_cancel.jpg b/pict/menu_b_cancel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e0de235f772b19076b3f91d97df469dc27bf0bb Binary files /dev/null and b/pict/menu_b_cancel.jpg differ diff --git a/pict/menu_b_ccls.jpg b/pict/menu_b_ccls.jpg new file mode 100644 index 0000000000000000000000000000000000000000..879bcd53726cc6576bf795afe1c6833401373bbf Binary files /dev/null and b/pict/menu_b_ccls.jpg differ diff --git a/pict/menu_b_close.jpg b/pict/menu_b_close.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ebed7dd19b09858c175852c608d8dcaa91c64ffd Binary files /dev/null and b/pict/menu_b_close.jpg differ diff --git a/pict/menu_b_cnew.jpg b/pict/menu_b_cnew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f4fbf24758bef9998596578ee224b2aed0f9706c Binary files /dev/null and b/pict/menu_b_cnew.jpg differ diff --git a/pict/menu_b_copen.jpg b/pict/menu_b_copen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4df3f4771bf4fc551bae6e2934db4bd4cd8ee28d Binary files /dev/null and b/pict/menu_b_copen.jpg differ diff --git a/pict/menu_b_fs.jpg b/pict/menu_b_fs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..32bcd63ceedf6b8005cc8deaafcb423e9cc9bf07 Binary files /dev/null and b/pict/menu_b_fs.jpg differ diff --git a/pict/menu_b_hashdb.jpg b/pict/menu_b_hashdb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffd2a995d08009e54a563bcbe782d6505aa7ffa8 Binary files /dev/null and b/pict/menu_b_hashdb.jpg differ diff --git a/pict/menu_b_hcls.jpg b/pict/menu_b_hcls.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ef9ec634ab402ed689051374a6f6121ce25de60 Binary files /dev/null and b/pict/menu_b_hcls.jpg differ diff --git a/pict/menu_b_help.jpg b/pict/menu_b_help.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6a0d06591a801fbc9f0377b36d66c0938125353d Binary files /dev/null and b/pict/menu_b_help.jpg differ diff --git a/pict/menu_b_hnew.jpg b/pict/menu_b_hnew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8cf2e8c5ab3ed2176e60049c7f46871968647629 Binary files /dev/null and b/pict/menu_b_hnew.jpg differ diff --git a/pict/menu_b_ifnew.jpg b/pict/menu_b_ifnew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec8355f99bf052a53827ac0f6e2763d5b1d16d72 Binary files /dev/null and b/pict/menu_b_ifnew.jpg differ diff --git a/pict/menu_b_inew.jpg b/pict/menu_b_inew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8f990e32d8fd0872762f97fe119da665ab44383e Binary files /dev/null and b/pict/menu_b_inew.jpg differ diff --git a/pict/menu_b_int.jpg b/pict/menu_b_int.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5fc0af25213a370f00e24004df8ca07d75d56b0b Binary files /dev/null and b/pict/menu_b_int.jpg differ diff --git a/pict/menu_b_menu.jpg b/pict/menu_b_menu.jpg new file mode 100644 index 0000000000000000000000000000000000000000..124a7e098032178d7696048a7a798b9d11a29c06 Binary files /dev/null and b/pict/menu_b_menu.jpg differ diff --git a/pict/menu_b_next.jpg b/pict/menu_b_next.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c1f2c254ff6ec2b1e6644fd7264e4e4ad77ca42e Binary files /dev/null and b/pict/menu_b_next.jpg differ diff --git a/pict/menu_b_note.jpg b/pict/menu_b_note.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eeea7bb1012d71b132c88b9d0fd09f8622cc4068 Binary files /dev/null and b/pict/menu_b_note.jpg differ diff --git a/pict/menu_b_ok.jpg b/pict/menu_b_ok.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e682233fb41bf3932041dbbbfcb97a2ea281ad1f Binary files /dev/null and b/pict/menu_b_ok.jpg differ diff --git a/pict/menu_b_ref.jpg b/pict/menu_b_ref.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a7a4329df9430703aad90c5cabcc77823a6b2326 Binary files /dev/null and b/pict/menu_b_ref.jpg differ diff --git a/pict/menu_b_rem.jpg b/pict/menu_b_rem.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a6f73498d1ad3ba02768b161fdda1a745dfe29c Binary files /dev/null and b/pict/menu_b_rem.jpg differ diff --git a/pict/menu_b_seq.jpg b/pict/menu_b_seq.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d903d25a19d24bd2b2718e4fea2d0ba775c31593 Binary files /dev/null and b/pict/menu_b_seq.jpg differ diff --git a/pict/menu_b_tl.jpg b/pict/menu_b_tl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..212be616c47567848cfbdabfbcc6990b05874173 Binary files /dev/null and b/pict/menu_b_tl.jpg differ diff --git a/pict/menu_h_cdet.jpg b/pict/menu_h_cdet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9bcfa77e405814fabfb2ade971b6ed41d024d0f Binary files /dev/null and b/pict/menu_h_cdet.jpg differ diff --git a/pict/menu_h_cnew.jpg b/pict/menu_h_cnew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9488016824ea0d4f837dbf2e670dc77a1f833bf9 Binary files /dev/null and b/pict/menu_h_cnew.jpg differ diff --git a/pict/menu_h_hdet.jpg b/pict/menu_h_hdet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..55d96bb1cee2a6b6bdd3eca8b9d45eb1489522e6 Binary files /dev/null and b/pict/menu_h_hdet.jpg differ diff --git a/pict/menu_h_hnew.jpg b/pict/menu_h_hnew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0293ccd5c62ecc4c48d6238adcb2c631f2e25a9 Binary files /dev/null and b/pict/menu_h_hnew.jpg differ diff --git a/pict/menu_h_idet.jpg b/pict/menu_h_idet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dff892fa72ddb121eaf8c230ad3fc93109c6dfa3 Binary files /dev/null and b/pict/menu_h_idet.jpg differ diff --git a/pict/menu_h_inew.jpg b/pict/menu_h_inew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1671f20cc442b7d4298de592b52262ba5bd13839 Binary files /dev/null and b/pict/menu_h_inew.jpg differ diff --git a/pict/menu_t_cg_cur.jpg b/pict/menu_t_cg_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ace65321d056d8e3adc9e976c57627de4d29d79 Binary files /dev/null and b/pict/menu_t_cg_cur.jpg differ diff --git a/pict/menu_t_cg_link.jpg b/pict/menu_t_cg_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f758b5cdd58d519f0ef017a70f068c6b6018f39 Binary files /dev/null and b/pict/menu_t_cg_link.jpg differ diff --git a/pict/menu_t_cg_org.jpg b/pict/menu_t_cg_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..152db199670b79cb3b70a533c27f0d74a1973628 Binary files /dev/null and b/pict/menu_t_cg_org.jpg differ diff --git a/pict/menu_t_hg_cur.jpg b/pict/menu_t_hg_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72cb1fbacd8a0a8f1f76c66ee66ae857d9f0e470 Binary files /dev/null and b/pict/menu_t_hg_cur.jpg differ diff --git a/pict/menu_t_hg_link.jpg b/pict/menu_t_hg_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..97f79c8f3e9711cab83fd9f94c45c1f01d743016 Binary files /dev/null and b/pict/menu_t_hg_link.jpg differ diff --git a/pict/menu_t_hg_org.jpg b/pict/menu_t_hg_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37f4b110451a7a88bc2f464f6aaa79aa2714a2b2 Binary files /dev/null and b/pict/menu_t_hg_org.jpg differ diff --git a/pict/menu_t_hm_cur.jpg b/pict/menu_t_hm_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67a9ee4699f6a9990705b25459aafca434c20e85 Binary files /dev/null and b/pict/menu_t_hm_cur.jpg differ diff --git a/pict/menu_t_hm_link.jpg b/pict/menu_t_hm_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e17433ea435378aeb2828ad57d7922fd714a995 Binary files /dev/null and b/pict/menu_t_hm_link.jpg differ diff --git a/pict/menu_t_hm_org.jpg b/pict/menu_t_hm_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5fb204ae57489b7bd2e863b671f7c5cf5bb4d913 Binary files /dev/null and b/pict/menu_t_hm_org.jpg differ diff --git a/pict/sanit_b_norm.jpg b/pict/sanit_b_norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a86e0e6ae06c8077435a206fc313c4bc98f3cec Binary files /dev/null and b/pict/sanit_b_norm.jpg differ diff --git a/pict/sanit_b_san.jpg b/pict/sanit_b_san.jpg new file mode 100644 index 0000000000000000000000000000000000000000..601b8d67c04ef93b1649be0b0bc2451cd64e9e9e Binary files /dev/null and b/pict/sanit_b_san.jpg differ diff --git a/pict/sanitized.jpg b/pict/sanitized.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bec0da750b1534fe54a516f8d0e25c13481879aa Binary files /dev/null and b/pict/sanitized.jpg differ diff --git a/pict/srch_b_lorig.jpg b/pict/srch_b_lorig.jpg new file mode 100644 index 0000000000000000000000000000000000000000..01354eec4e47d6e7396dddbdce76a355eb908cf0 Binary files /dev/null and b/pict/srch_b_lorig.jpg differ diff --git a/pict/srch_b_lun.jpg b/pict/srch_b_lun.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a2d2b720e6ecd7a8e1ff14d8e2ad1cb15328b3d Binary files /dev/null and b/pict/srch_b_lun.jpg differ diff --git a/pict/srch_b_str.jpg b/pict/srch_b_str.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d9371452a5001e11945c3cfc67f0fa209d8eb93 Binary files /dev/null and b/pict/srch_b_str.jpg differ diff --git a/pict/srch_b_un.jpg b/pict/srch_b_un.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1a4b4d2da8b8d38d84f163fd8d406711676cca9 Binary files /dev/null and b/pict/srch_b_un.jpg differ diff --git a/pict/tab_close.jpg b/pict/tab_close.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a63dd24ba231299168472dea3524db52727b4f1 Binary files /dev/null and b/pict/tab_close.jpg differ diff --git a/pict/tab_help.jpg b/pict/tab_help.jpg new file mode 100644 index 0000000000000000000000000000000000000000..632e27006b00f6a18c51e5b20f31ed9dede5de0a Binary files /dev/null and b/pict/tab_help.jpg differ diff --git a/pict/tl_t_data_cur.jpg b/pict/tl_t_data_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d9d9b10638a30653b203b5f99107d915ca007465 Binary files /dev/null and b/pict/tl_t_data_cur.jpg differ diff --git a/pict/tl_t_data_link.jpg b/pict/tl_t_data_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe8333bf274b8acf0ad68cea2502698ad53dabcf Binary files /dev/null and b/pict/tl_t_data_link.jpg differ diff --git a/pict/tl_t_notes_link.jpg b/pict/tl_t_notes_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe01d8c05554a886039c72e519a95283e67279e8 Binary files /dev/null and b/pict/tl_t_notes_link.jpg differ diff --git a/pict/tl_t_notes_org.jpg b/pict/tl_t_notes_org.jpg new file mode 100644 index 0000000000000000000000000000000000000000..03c643b84cc6f49571ff4f2219c4107ce28e2c45 Binary files /dev/null and b/pict/tl_t_notes_org.jpg differ diff --git a/pict/tl_t_tl_cur.jpg b/pict/tl_t_tl_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ed69d8bb2511b86e499b460d817bbddf9844a8d4 Binary files /dev/null and b/pict/tl_t_tl_cur.jpg differ diff --git a/pict/tl_t_tl_link.jpg b/pict/tl_t_tl_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1da8edd58df6f0a30a0377d819670d82303ab23f Binary files /dev/null and b/pict/tl_t_tl_link.jpg differ diff --git a/pict/tl_t_view_cur.jpg b/pict/tl_t_view_cur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b621d7d857b3e437f9b7da200f40138522aef2c4 Binary files /dev/null and b/pict/tl_t_view_cur.jpg differ diff --git a/pict/tl_t_view_link.jpg b/pict/tl_t_view_link.jpg new file mode 100644 index 0000000000000000000000000000000000000000..94016071364e8948b80a50593f21f3e89fbd822e Binary files /dev/null and b/pict/tl_t_view_link.jpg differ diff --git a/xcode/autopsy.xcodeproj/project.pbxproj b/xcode/autopsy.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000000000000000000000000000000..f04caa4f2f1d6538933554471790f516a69dae43 --- /dev/null +++ b/xcode/autopsy.xcodeproj/project.pbxproj @@ -0,0 +1,138 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXFileReference section */ + 02B041C40E9070E400E46A87 /* .perltidyrc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = .perltidyrc; sourceTree = "<group>"; }; + 02B041C50E9070E400E46A87 /* Appsort.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Appsort.pm; sourceTree = "<group>"; }; + 02B041C60E9070E400E46A87 /* Appview.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Appview.pm; sourceTree = "<group>"; }; + 02B041C70E9070E400E46A87 /* Args.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Args.pm; sourceTree = "<group>"; }; + 02B041C80E9070E400E46A87 /* Caseman.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Caseman.pm; sourceTree = "<group>"; }; + 02B041C90E9070E400E46A87 /* Data.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Data.pm; sourceTree = "<group>"; }; + 02B041CA0E9070E400E46A87 /* define.pl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = define.pl; sourceTree = "<group>"; }; + 02B041CB0E9070E400E46A87 /* Exec.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Exec.pm; sourceTree = "<group>"; }; + 02B041CC0E9070E400E46A87 /* File.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = File.pm; sourceTree = "<group>"; }; + 02B041CD0E9070E400E46A87 /* Filesystem.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Filesystem.pm; sourceTree = "<group>"; }; + 02B041CE0E9070E400E46A87 /* Frame.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Frame.pm; sourceTree = "<group>"; }; + 02B041CF0E9070E400E46A87 /* Fs.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Fs.pm; sourceTree = "<group>"; }; + 02B041D00E9070E400E46A87 /* Hash.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Hash.pm; sourceTree = "<group>"; }; + 02B041D10E9070E400E46A87 /* Kwsrch.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Kwsrch.pm; sourceTree = "<group>"; }; + 02B041D20E9070E400E46A87 /* Main.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Main.pm; sourceTree = "<group>"; }; + 02B041D30E9070E400E46A87 /* Meta.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Meta.pm; sourceTree = "<group>"; }; + 02B041D40E9070E400E46A87 /* Notes.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Notes.pm; sourceTree = "<group>"; }; + 02B041D50E9070E400E46A87 /* Print.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Print.pm; sourceTree = "<group>"; }; + 02B041D60E9070E400E46A87 /* search.pl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = search.pl; sourceTree = "<group>"; }; + 02B041D70E9070E400E46A87 /* Timeline.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Timeline.pm; sourceTree = "<group>"; }; + 02B041D80E9070E400E46A87 /* Vs.pm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = Vs.pm; sourceTree = "<group>"; }; + 02B041DA0E90710300E46A87 /* .perltidyrc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = .perltidyrc; sourceTree = "<group>"; }; + 02B041DB0E90710300E46A87 /* autopsy.base */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = autopsy.base; sourceTree = "<group>"; }; + 02B041DD0E90710400E46A87 /* make-live-cd.base */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = "make-live-cd.base"; sourceTree = "<group>"; }; + 02EF3BBF0860F668001CA8B0 /* global.css */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = global.css; path = ../global.css; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 02670C57079A2F7E00BA95F3 = { + isa = PBXGroup; + children = ( + 02B041D90E90710300E46A87 /* base */, + 02B041C30E9070E400E46A87 /* lib */, + 02EF3BBF0860F668001CA8B0 /* global.css */, + ); + sourceTree = "<group>"; + }; + 02B041C30E9070E400E46A87 /* lib */ = { + isa = PBXGroup; + children = ( + 02B041C40E9070E400E46A87 /* .perltidyrc */, + 02B041C50E9070E400E46A87 /* Appsort.pm */, + 02B041C60E9070E400E46A87 /* Appview.pm */, + 02B041C70E9070E400E46A87 /* Args.pm */, + 02B041C80E9070E400E46A87 /* Caseman.pm */, + 02B041C90E9070E400E46A87 /* Data.pm */, + 02B041CA0E9070E400E46A87 /* define.pl */, + 02B041CB0E9070E400E46A87 /* Exec.pm */, + 02B041CC0E9070E400E46A87 /* File.pm */, + 02B041CD0E9070E400E46A87 /* Filesystem.pm */, + 02B041CE0E9070E400E46A87 /* Frame.pm */, + 02B041CF0E9070E400E46A87 /* Fs.pm */, + 02B041D00E9070E400E46A87 /* Hash.pm */, + 02B041D10E9070E400E46A87 /* Kwsrch.pm */, + 02B041D20E9070E400E46A87 /* Main.pm */, + 02B041D30E9070E400E46A87 /* Meta.pm */, + 02B041D40E9070E400E46A87 /* Notes.pm */, + 02B041D50E9070E400E46A87 /* Print.pm */, + 02B041D60E9070E400E46A87 /* search.pl */, + 02B041D70E9070E400E46A87 /* Timeline.pm */, + 02B041D80E9070E400E46A87 /* Vs.pm */, + ); + name = lib; + path = ../lib; + sourceTree = SOURCE_ROOT; + }; + 02B041D90E90710300E46A87 /* base */ = { + isa = PBXGroup; + children = ( + 02B041DA0E90710300E46A87 /* .perltidyrc */, + 02B041DB0E90710300E46A87 /* autopsy.base */, + 02B041DD0E90710400E46A87 /* make-live-cd.base */, + ); + name = base; + path = ../base; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXProject section */ + 02670C5B079A2F7E00BA95F3 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 027355510E7C4D6D002BD6DB /* Build configuration list for PBXProject "autopsy" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 1; + mainGroup = 02670C57079A2F7E00BA95F3; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + 027355520E7C4D6D002BD6DB /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Development; + }; + 027355530E7C4D6D002BD6DB /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Deployment; + }; + 027355540E7C4D6D002BD6DB /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 027355510E7C4D6D002BD6DB /* Build configuration list for PBXProject "autopsy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 027355520E7C4D6D002BD6DB /* Development */, + 027355530E7C4D6D002BD6DB /* Deployment */, + 027355540E7C4D6D002BD6DB /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = 02670C5B079A2F7E00BA95F3 /* Project object */; +}