#!/usr/bin/perl # # Chaosreader can trace TCP/UDP/... sessions and fetch application data # from tcpdump or snoop logs. This is like an "any-snarf" program, it will # fetch telnet sessions, FTP files, HTTP transfers (HTML, GIF, JPEG, ...), # SMTP emails, etc ... from the captured data inside the network traffic # logs. It creates a html index file that links to all the session details, # including realtime replay programs for telnet, rlogin or IRC sessions; # and reports such as image reports and HTTP GET/POST content reports. # It also creates replay programs for telnet sessions, so that you can # play them back in realtime (or even different speeds). # # Chaosreader can also run in standalone mode - where it invokes tcpdump or # snoop (if they are available) to create the log files and then processes # them. # # # 05-May-2004, ver 0.94 (check for new versions, http://www.brendangregg.com) # (or run a web search for "chaosreader") # # # QUICK USAGE: # tcpdump -s9000 -w out1; chaosreader out1; netscape index.html # or, # snoop -o out1; chaosreader out1; netscape index.html # or, # ethereal (save as "out1"); chaosreader out1; netscape index.html # or, # chaosreader -s 5; netscape index.html # # # USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir] # [-b port[,...]] [-B port[,...]] # [-j IPaddr[,...]] [-J IPaddr[,...]] # [-l port[,...]] [-L port[,...]] [-m bytes[k]] # [-M bytes[k]] [-o "time"|"size"|"type"|"ip"] # [-p port[,...]] [-P port[,...]] # infile [infile2 ...] # # chaosreader -s [mins] | -S [mins[,count]] # [-z] [-f 'filter'] # # chaosreader # Create application session files, indexes # # -a, --application # Create application session files (default) # -e, --everything # Create HTML 2-way & hex files for everything # -h # Print a brief help # --help # Print verbose help (this) and version # --help2 # Print massive help # -i, --info # Create info file # -q, --quiet # Quiet, no output to screen # -r, --raw # Create raw files # -v, --verbose # Verbose - Create ALL files .. (except -e) # -x, --index # Create index files (default) # -A, --noapplication # Exclude application session files # -H, --hex # Include hex dumps (slow) # -I, --noinfo # Exclude info files # -R, --noraw # Exclude raw files # -T, --notcp # Exclude TCP traffic # -U, --noudp # Exclude UDP traffic # -Y, --noicmp # Exclude ICMP traffic # -X, --noindex # Exclude index files # -k, --keydata # Create extra files for keystroke analysis # -D dir --dir dir # Output all files to this directory # -b 25,79 --playtcp 25,79 # replay these TCP ports as well (playback) # -B 36,42 --playudp 36,42 # replay these UDP ports as well (playback) # -l 7,79 --htmltcp 7,79 # Create HTML for these TCP ports as well # -L 7,123 --htmludp 7,123 # Create HTML for these UDP ports as well # -m 1k --min 1k # Min size of connection to save ("k" for Kb) # -M 1024k --max 1k # Max size of connection to save ("k" for Kb) # -o size --sort size # sort Order: time/size/type/ip (Default time) # -p 21,23 --port 21,23 # Only examine these ports (TCP & UDP) # -P 80,81 --noport 80,81 # Exclude these ports (TCP & UDP) # -s 5 --runonce 5 # Standalone. Run tcpdump/snoop for 5 mins. # -S 5,10 --runmany 5,10 # Standalone, many. 10 samples of 5 mins each. # -S 5 --runmany 5 # Standalone, endless. 5 min samples forever. # -z --runredo # Standalone, redo. Rereads last run's logs. # -j 10.1.2.1 --ipaddr 10.1.2.1 # Only examine these IPs # -J 10.1.2.1 --noipaddr 10.1.2.1 # Exclude these IPs # -f 'port 7' --filter 'port 7' # With standalone, use this dump filter. # # eg1, # tcpdump -s9000 -w output1 # create tcpdump capture file # chaosreader output1 # extract recognised sessions, or, # chaosreader -ve output1 # gimme everything, or, # chaosreader -p 20,21,23 output1 # only ftp and telnet... # eg2, # snoop -o output1 # create snoop capture file instead # chaosreader output1 # extract recognised sessions... # eg3, # chaosreader -S 2,5 # Standalone, sniff network 5 times for 2 mins # # each. View index.html for progress (or .text) # # Output Files: Many will be created, run this in a clean directory. # Short example, # index.html Html index (full details) # index.text Text index # index.file File index for standalone redo mode # image.html HTML report of images # getpost.html HTML report of HTTP GET/POST requests # session_0001.info Info file describing TCP session #1 # session_0001.telnet.html HTML coloured 2-way capture (time sorted) # session_0001.telnet.raw Raw data 2-way capture (time sorted) # session_0001.telnet.raw1 Raw 1-way capture (assembeled) server->client # session_0001.telnet.raw2 Raw 1-way capture (assembeled) client->server # session_0002.web.html HTML coloured 2-way # session_0002.part_01.html HTTP portion of the above, a HTML file # session_0003.web.html HTML coloured 2-way # session_0003.part_01.jpeg HTTP portion of the above, a JPEG file # session_0004.web.html HTML coloured 2-way # session_0004.part_01.gif HTTP portion of the above, a GIF file # session_0005.part_01.ftp-data.gz An FTP transfer, a gz file. # ... # The convention is, # session_* TCP Sessions # stream_* UDP Streams # icmp_* ICMP packets # index.html HTML Index # index.text Text Index # index.file File Index for standalone redo mode only # image.html HTML report of images # getpost.html HTML report of HTTP GET/POST requests # *.info Info file describing the Session/Stream # *.raw Raw data 2-way capture (time sorted) # *.raw1 Raw 1-way capture (assembeled) server->client # *.raw2 Raw 1-way capture (assembeled) client->server # *.replay Session replay program (perl) # *.partial.* Partial capture (tcpdump/snoop were aware of drops) # *.hex.html 2-way Hex dump, rendered in coloured HTML # *.hex.text 2-way Hex dump in plain text # *.X11.replay X11 replay script (talks X11) # *.textX11.replay X11 communicated text replay script (text only) # *.textX11.html 2-way text report, rendered in red/blue HTML # *.keydata Keystroke delay data file. Used for SSH analysis. # # Modes: # * Normal - eg "chaosreader infile", this is where a tcpdump/snoop file # was created previously and chaosreader reads and processes it. # * Standalone, once - eg "chaosreader -s 10", this is where chaosreader # runs tcpdump/snoop and generates the log file, in this case for 10 i # minutes, and then processes the result. Some OS's may not have # tcpdump or snoop available so this will not work (instead you may be # able to get Ethereal, run it, save to a file, then use normal mode). # There is a master index.html and the report index.html in a sub dir, # which is of the format out_YYYYMMDD-hhmm, eg "out_20031003-2221". # * Standalone, many - eg "chaosreader -S 5,12", this is where chaosreader # runs tcpdump/snoop and generates many log files, in this case it # samples 12 times for 5 minutes each. While this is running, the master # index.html can be viewed to watch progress, which links to minor # index.html reports in each sub directory. # * Standalone, redo - eg "chaosreader -ve -z", (the -z), this is where # a standalone capture was previously performed - and now you would like # to reprocess the logs - perhaps with different options (in this case, # "-ve"). It reads index.file to determine which capture logs to read. # * Standalone, endless - eg "chaosreader -S 5", like standalone many - # but runs forever (if you ever had the need?). Watch your disk space! # # Note: this is a work in progress, some of the code is a little unpolished. # # Advice: # * Run chaosreader in an empty directory. # * Create small packet dumps. Chaosreader uses around 5x the dump size # in memory. A 100Mb file could need 500Mb of RAM to process. # * Your tcpdump may allow "-s0" (entire packet) instead of "-s9000". # * Beware of using too much disk space, especially standalone mode. # * If you capture too many small connections giving a huge index.html, # try using the -m option to ignore small connections. eg "-m 1k". # * snoop logs may actually work better. Snoop logs are based on RFC1761, # however there are many varients of tcpdump/libpcap and this program # cannot read them all. If you have Ethereal you can create snoop logs # during the "save as" option. On Solaris use "snoop -o logfile". # * tcpdump logs may not be portable between OSs that use different sized # timestamps or endian. # * Logs are best created in a memory filesystem for speed, usually /tmp. # * For X11 or VNC playbacks, first practise by replaying a recent captured # session of your own. The biggest problem is colour depth, your screen # must match the capture. For X11 check authentication (xhost +), for # VNC check the viewers options (-8bit, "Hextile", ...) # * SSH analysis can be performed with the "sshkeydata" program as # demonstrated on http://www.brendangregg.com/sshanalysis.html . # chaosreader provides the input files (*.keydata) that sshkeydata # analyses. # # Bugs: The following assumptions may cause problems (check for new vers); # * A lower port number = the service type. Eg with ports 31247 and 23, # the actual type of session is telnet (23). This may not work for # some things (eg, VNC). # * Time based order is more important for 2-way sessions (eg telnet), # SEQ order is more import for 1-way transfers (eg ftp-data). # * One particular TCP session isn't active for long enough that the SEQ # number loops (or even wraps). # # WARNING: Please don't use this software for anything illegal. That definition # differs for every country, please check the law first. # This is a great network troubleshooting and development tool, not a # "cracking" or "hacking" tool - a misidentification that could render owning # this software illegal in some countries. # # SEE ALSO: ethereal (GUI packet viewer), dsniff (sniffing toolkit) # # COPYRIGHT: Copyright (c) 2003, 2004 Brendan Gregg. # # 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. # # (http://www.gnu.org/copyleft/gpl.html) # # Author: Brendan Gregg [Sydney, Australia] # # Todo: # * Rework code to improve structure. # * Add more application protocol filters. ARP, RARP. # * Ensure current application filters are robust (more testing). # * Process captured filenames from FTP, HTTP and NFS transfers. # * Add more file types (magic numbers/frequency analysis). # * Process more IPv6 extension headers, ICMP types. # # 28-Sep-2003 Brendan Gregg Began writing this. # 08-Oct-2003 " " Released version 0.7 beta # 09-Oct-2003 " " Added telnet replays # 12-Oct-2003 " " Added IRC ports and replays # 19-Oct-2003 " " Made code more robust on different OSs # 01-Nov-2003 " " Code cleanup, complex data types, IPv6, ICMP # 03-Nov-2003 " " Added Standalone mode, standalone redo, ... # 05-Nov-2003 " " Added Image indexes, GETPOST indexes # 15-Nov-2003 " " Added HTTP proxy style log, hex dumps # 27-Jan-2004 " " Released experimental X11 & VNC processing # 30-Mar-2004 " " 802.11b, sorts, less RAM used, tun packets. # 01-May-2004 " " CLI enhanced, faster, SSH analysis. use Getopt::Long; use Benchmark; ##################### # --- Variables --- # # # Some defaults # $PERL = "/usr/bin/perl"; # perl path for replay scripts $integerSize = length(pack('I',0)); # can make a difference for tcpdumps $the_date = scalar localtime(); # this is printed in the reports $WRAP = 108; # wordwrap chars $BM = 0; # benchmark counter $| = 1; # flush output # # The following is needed for old perl5 multiline matching. New perl5 uses # a "/s" on the RE (which is used in this program as well). # $* = 1; # old perl5 # # These ports have been selected to be saved as coloured 2-way HTML files # @Save_As_HTML_TCP_Ports = (21,23,25,79,80,109,110,119,143,513,514,1080, 3128,4110,5000,5555,6660,6665,6666,6667,6668,7000,8000,8080,9000); @Save_As_HTML_UDP_Ports = (53); # # These ports have been selected to be saved as realtime playback scripts # (telnet, login, and numerous IRC ports) # @Save_As_TCP_Playback_Ports = (23,513,4110,5000,5555,6660,6666,6667, 6668,7000,8000,9000); @Save_As_UDP_Playback_Ports = (7); # # These are the X11 ports to save as X11 playback scripts # @Save_As_X11_Playback_Ports = (6000,6001,6002,6003,6004,6005,6006,6007); # # These X11 ports will have the text saved as coloured 2-way HTML files # @Save_As_HTML_X11_Ports = (6000,6001,6002,6003,6004,6005,6006,6007); # # These are the VNC ports to save as VNC playback scripts # @Save_As_VNC_Playback_Ports = (5900,5901,5902,5903,5904,5905,5906,5907); # # --- Arguments --- # &Process_Command_Line_Arguments(); ### Record program start $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Program Start"; # # Load some lookup tables for number -> name translations. # &Load_Etc_Services(); &Set_IP_Protocols(); &Set_ICMP_Types(); &Set_Result_Names(); &Set_X11_Codes(); &Set_X11_KeyCodes(); &Set_VNC_Codes(); ########################### # --- MODE 1 - Normal --- # ########################### # # Process log files, # if ($Arg{normal}) { # # Initial values # $frame = 0; $number = 0; %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); ### Print version &Print_Welcome(); ###################################### # --- INPUT - Read Packet Log(s) --- # foreach $filename (@{$Arg{infiles}}) { # # Check input file type and Open # &Open_Input_File($filename); # # Read though the entire input file, saving all packet # data in memory (mainly %TCP and %UDP). # &Read_Input_File(); } ############################################# # --- OUTPUT - Process TCP/UDP Sessions --- # ### cd to output &Chdir($Arg{output_dir}); &Print_Header2(); ### Determine Session and Stream time order %Index = (); %Image = (); %GETPOST = (); &Sort_Index(); # # Process %TCP and create session* output files, write %Index # &Process_TCP_Sessions(); # # Process %UDP and create session* output files, write %Index # &Process_UDP_Streams(); # # Process %ICMP # &Process_ICMP(); # # Create Index Files from %Index # &Create_Index_Files(); &Create_Log_Files(); ############### # --- END --- # &Print_Footer1(); } ############################### # --- MODE 2 - Standalone --- # ############################### elsif ($Arg{standalone}) { ############################################################ # --- STANDALONE - Create Packet Logs and Process them --- # $limit = $Arg{count}; $filenum = 0; ### Check for the sniffer command &Check_Command(); ### cd to output &Chdir($Arg{output_dir}); ### Print welcome &Print_Welcome(); # # MAIN LOOP # while ($limit != 0) { # # Create a meaningful directory and filename # @Times = localtime(); $dirname = sprintf("out_%d%02d%02d-%02d%02d",($Times[5]+1900), $Times[4],$Times[3],$Times[2],$Times[1]); $filename = "$dirname.log"; # # Initial values # $frame = 0; $number = 0; %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); # # Record details in a Master Index # $Master[$filenum]{starttime} = scalar localtime(); $Master[$filenum]{duration} = - time(); # will +end time $Master[$filenum]{dir} = $dirname; $Master[$filenum]{file} = $filename; # # Create and cd to output dir # mkdir ("$dirname",0755) || die "ERROR01: Couldn't mkdir (perms?): $!\n"; chdir "$dirname" || die "ERROR02: Couldn't cd $dirname: $!\n"; print "\nCreating log: $dirname/$filename\n" unless $Arg{quiet}; # # fork, so that one process can exec tcpdump/snoop while the other # sleeps and then kills it. # $pid = fork(); die "ERROR03: Can't fork (resources?): $!\n" if (! defined $pid); if ($pid == 0) { ############################### # --- CREATE - Packet Log --- # print "Running: $command $filename $Arg{filter}\n" unless $Arg{quiet}; ### exec, so $pid points to sniffer exec("$command $filename $Arg{filter}") && die "ERROR04: couldn't run $command file: $!\n"; } else { ### Wait for logfile to be populated sleep($Arg{mins} * 60); ### Kill child (TERM, INT) kill 15, $pid; kill 2, $pid; } exit if $pid == 0; # check for impossibility ### Record end time, duration, size $Master[$filenum]{endtime} = scalar localtime(); $Master[$filenum]{duration} += time(); # finish writing the log before reading it's size system("sync") if (($^O eq "linux") || ($^O eq "solaris")); $Master[$filenum]{size} = -s "$filename"; print "\nProcessing: $dirname/$filename\n" unless $Arg{quiet}; $bak = $Arg{quiet}; $Arg{quiet} = 1; ############################### # --- INPUT - Process Log --- # &Open_Input_File($filename); ### Populate memory (%TCP, %UDP, ...). &Read_Input_File(); ############################################# # --- OUTPUT - Process TCP/UDP Sessions --- # ### Determine Session and Stream time order %Index = (); %Image = (); %GETPOST = (); &Sort_Index(); ### Process %TCP, %UDP, ..., create output fies, write %Index &Process_TCP_Sessions(); &Process_UDP_Streams(); &Process_ICMP(); ### Create Index Files from %Index &Create_Index_Files(); &Create_Log_Files(); chdir ".." || die "ERROR05: Couldn't cd ..: $!\n"; $Arg{quiet} = $bak; ### Create Master Index from @Master &Create_Index_Master(); $limit--; $filenum++; } } ########################## # --- MODE 3 - Redo --- # ########################## elsif ($Arg{redo}) { ############################################################# # --- STANDALONE REDO - Redo last run from sniffer logs --- # $filenum = 0; ### Read index.file for logs to process &Load_Index_File(); ### Print welcome &Print_Welcome(); # # MAIN LOOP # for ($index=0; $index <= $#Master; $index++) { ### Get previous run values $dirname = $Master[$index]{dir}; $filename = $Master[$index]{file}; ### Initial values $frame = 0; $number = 0; %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); ### Create and cd to output dir chdir "$dirname" || die "ERROR06: Couldn't cd $dirname: $!\n"; print "Processing: $dirname/$filename\n" unless $Arg{quiet}; $bak = $Arg{quiet}; $Arg{quiet} = 1; ############################### # --- INPUT - Process Log --- # &Open_Input_File($filename); ### Populate memory (%TCP, %UDP, ...). &Read_Input_File(); ############################################# # --- OUTPUT - Process TCP/UDP Sessions --- # ### Determine Session and Stream time order %Index = (); %Image = (); %GETPOST = (); &Sort_Index(); ### Process %TCP, %UDP, ..., create output fies, write %Index &Process_TCP_Sessions(); &Process_UDP_Streams(); &Process_ICMP(); ### Create Index Files from %Index &Create_Index_Files(); &Create_Log_Files(); chdir ".." || die "ERROR07: Couldn't cd ..: $!\n"; $Arg{quiet} = $bak; $limit--; $filenum++; } ### Create Master Index from @Master &Create_Index_Master(); } # # BENCHMARK REPORT # if ($Arg{bench}) { $Bench{++$BM}{mark} = new Benchmark; $Bench{$BM}{text} = "Program End"; print "\nBenchmarks,\n\n"; for ($bm=1; $bm <= $BM; $bm++) { $bdiff = timediff($Bench{$bm}{mark},$Bench{1}{mark}); printf(" %-32s %s\n",$Bench{$bm}{text},timestr($bdiff)); } } ##################### # --- SUBROUTINES --- # (Most of these subroutines are used as shortcuts to code, not traditional # scoped subroutines as with other languages) # Open_Input_File - open the packet log specified. This checks the header # of the file to determine whether it is a tcpdump/libpcap or snoop # log (including several styles of tcpdump/libpcap). # sub Open_Input_File { my $infile = shift; my ($length,$size); $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Open Input File"; print "Opening, $infile\n\n" unless $Arg{quiet}; # # Open packet log # open(INFILE,$infile) || die "Can't open $infile: $!\n"; binmode(INFILE); # for backward OSs # # Fetch header # $length = read(INFILE,$header,8); die "ERROR08: Can't read from $infile\n" if $length < 8; ### Print status print "Reading file contents,\n" unless $Arg{quiet}; $SIZE = -s $infile; # # Try to determine if this is a tcpdump or a snoop file # ($ident) = unpack('a8',$header); if ($ident =~ /^snoop/) { $TYPE = "snoop"; $length = read(INFILE,$header,8); ($version,$type) = unpack('NN',$header); } elsif ($ident =~ /^\241\262\303\324|^\324\303\262\241/ || $ident =~ /^\241\262\315\064|^\064\315\262\241/) { $TYPE = "tcpdump"; $ident = unpack('a4',$header); # try again # standard/modified defines style, 1/2 defines endian if ($ident =~ /^\241\262\303\324/) { $STYLE = "standard1"; } if ($ident =~ /^\324\303\262\241/) { $STYLE = "standard2"; } if ($ident =~ /^\241\262\315\064/) { $STYLE = "modified1"; } if ($ident =~ /^\064\315\262\241/) { $STYLE = "modified2"; } if ($STYLE =~ /1$/) { # reread in big-endian ($ident,$major,$minor) = unpack('a4nn',$header); } else { # reread in little-endian ($ident,$major,$minor) = unpack('a4vv',$header); } # # Check tcpdump header carefully to ensure this is ver 2.4. # if ($major != 2 && $minor != 4) { # # Die if this is an unknown version. (there could # be new vers of tcpdump/libpcap in the future). # print STDERR "ERROR09: Wrong tcpdump version "; print STDERR "($version.$type).\n(expected 2.4).\n"; exit 1; } # # Nudge the filehandle past the rest of the header... # $length = read(INFILE,$header_rest,16); } else { # # Die - unknown file format # print STDERR "ERROR10: Input dosen't look like a tcpdump or "; print STDERR "snoop output file.\n\tIf it is tcpdump, it "; print STDERR "may be a wrong or new version.\n"; exit 1; } ### Record the filename into the global %Arg $Arg{infile} = $infile; } # Read_Input_File - this subroutine loops through the records in the packet # log, storing all the TCP and UDP data into %TCP and %UDP. (see the end # of the program for the structure of these data types). %Count is also # populated with various frequency counts. # sub Read_Input_File { my ($trailers,$pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length, $ppp_protocol,$wless_fc,$wless_version,$wless_type,$wless_duration, $wless_subtype,$wless_from,$wless_to,$wless_flag,$wless_WEP, $wless_bss,$wless_src,$wless_dest,$wless_cksum,$llc_head,$llc_control, $llc_org,$llc_type,$wless_OK,$bytes,$counter,$packets); $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Read Input File - start"; local $packet = 0; # counter if ($TYPE eq "snoop") { $bytes = 16; } else { $bytes = 24; } # # --- Pass #1, Store IP data in memory (%IP) -- # while (1) { # # --- Read Record from Log --- # if ($TYPE eq "snoop") { &Read_Snoop_Record(); # will "last" on error $packet_data = $snoop_data; $packet_time = $snoop_seconds; $packet_timefull = $snoop_seconds + $snoop_msecs/1000000; $record_size = $snoop_length_rec; } else { &Read_Tcpdump_Record(); # will "last" on error $packet_data = $tcpdump_data; $packet_time = $tcpdump_seconds; $packet_timefull = $tcpdump_seconds + $tcpdump_msecs/1000000; $record_size = $tcpdump_length + ($integerSize * 2 + 8); } ### Print status summary unless ($Arg{quiet}) { $bytes += $record_size; if (($packet % 16) == 0) { printf("%s %2.0f%% (%d/%d)","\b"x24, (100*$bytes/$SIZE),$bytes,$SIZE); } } # # --- Parse TCP/IP layers (a little ;) --- # #------------------------------------------------------------------- # # Wireless, 802.11b # $decoded = 0; # this flag is true if wireless was found # unpack a little first, (efficiency) ($wless_fc) = unpack('H4',$packet_data); # this matches on possible send or receive wireless traffic, however # this could also be the start of an 802.3 frame - making this part # of a MAC address. (The IEEE list on OUIs had these as unassigned). if ($wless_fc =~ /^080[1256]/) { # now dig deeper, # (this is one form of 802.11 - the form we are interested # in, however note that there is a lot more to 802.11). ($wless_fc,$wless_duration,$wless_bss,$wless_src, $wless_dest,$wless_cksum,$llc_head,$llc_control,$llc_org, $llc_type,$ether_data) = unpack('nnH12H12H12na2CH6H4a*',$packet_data); $wless_to = $wless_fc & 1; # Check this is IP and encapsulated Ethernet, if (($llc_type eq "0800") && ($llc_org eq "000000")) { ### Populate ether variables for use later on $ether_type = $llc_type; if ($wless_to) { $ether_dest = $wless_dest; $ether_src = $wless_src; } else { $ether_dest = $wless_src; $ether_src = $wless_dest; } $decoded = 1; # remember we did this } # (else try redecoding this using 802.3) } #------------------------------------------------------------------- # # Tun device # # unpack a little first, (efficiency) ($tun_id) = unpack('H8',$packet_data); # this checks if the frame looks like a tun device frame if ($tun_id eq "02000000") { # now dig deeper, ($tun_id,$ether_data) = unpack('a4a*',$packet_data); $ether_src = "0"; $ether_dest = "0"; $ether_type = "0800"; $decoded = 1; # remember we did this } #------------------------------------------------------------------- # # Ethernet, 802.3 # ### Unpack ether data ($ether_dest,$ether_src,$ether_type,$ether_data) = unpack('H12H12H4a*',$packet_data) unless $decoded; ### Count ether types seen $Count{EtherType}{$ether_type}++; $CountMaster{EtherType}{$ether_type}++; # # Process extended Ethernet types (wireless, PPPoE) # ### PPPoE if ($ether_type eq "8864") { ($pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length, $ppp_protocol,$ether_data) = unpack("CCnnna*",$ether_data); ### Skip anything but data (we just want data - code 0) next if $pppoe_code != 0; # (May like to add code here later to process $ppp_protocol, # eg, to process LCP). } elsif (($ether_type ne "0800") && ($ether_type ne "86dd")) { next; } #------------------------------------------------------------------- # # IP # ### Check for IP ver ($ip_verNihl,$ip_rest) = unpack('Ca*',$ether_data); $ip_ver = $ip_verNihl & 240; $ip_ver = $ip_ver >> 4; if ($ip_ver == 4) { #----------------------------------------------------------- # # IPv4 # ### Unpack IP data ($ip_verNihl,$ip_tos,$ip_length,$ip_ident,$ip_flagNfrag, $ip_ttl,$ip_protocol,$ip_checksum,@ip_src[0..3], @ip_dest[0..3],$ip_data) = unpack('CCnnnCCa2CCCCCCCCa*', $ether_data); ### Get frag and flag data $ip_frag = $ip_flagNfrag & 8191; $ip_flag = $ip_flagNfrag & 57344; $ip_flag = $ip_flag >> 13; $ip_MF = $ip_flag & 1; ### Strip off IP options if present $ip_ihl = $ip_verNihl & 15; $ip_ihl = $ip_ihl << 2; $ip_options_num = $ip_ihl - 20; if ($ip_options_num > 0) { ($ip_options,$ip_data) = unpack("a${ip_options_num}a*",$ip_data); } ### Strip off Ethernet trailers $ip_dlength = $ip_length - $ip_options_num - 20; ($ip_data,$trailers) = unpack("a${ip_dlength}a*",$ip_data); ### Build text strings of IP addresses $ip_src = sprintf("%u.%u.%u.%u",@ip_src); $ip_dest = sprintf("%u.%u.%u.%u",@ip_dest); } elsif ($ip_ver == 6) { #----------------------------------------------------------- # # IPv6 # ($ip_verNihl,$ip_flow,$ip_length,$ip_next,$ip_hop, @ip_src[0..15],@ip_dest[0..15],$ip_data) = unpack('Ca3nCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCa*', $ether_data); $ip_protocol = $ip_next; ### Build text strings of IP addresses $ip_src = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x", @ip_src); $ip_dest = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x", @ip_dest); ### Compress IPv6 text Address $ip_src =~ s/:00:/:0:/g; $ip_src =~ s/:00:/:0:/g; $ip_dest =~ s/:00:/:0:/g; $ip_dest =~ s/:00:/:0:/g; $ip_src =~ s/(:0)+/::/; $ip_dest =~ s/(:0)+/::/; # # Check for IPv6 Fragmentation (embedded) # if ($ip_protocol == 44) { ($ip_next,$ip_reserved,$ip_fragNmf,$ip_ident,$ip_data) = unpack('CCnNa*',$ip_data); $ip_protocol = $ip_next; $ip_MF = $ip_fragNmf & 1; $ip_frag = $ip_fragNmf >> 3; } else { $ip_MF = 0; $ip_ident = 0; $ip_frag = 0; } } else { ### Not IPv4 or IPv6 - could be LCP (skip for now) next; } ### Count IP Protocols seen $Count{IPprotocol}{$ip_protocol}++; $CountMaster{IPprotocol}{$ip_protocol}++; ### Count IP Addresses seen $Count{IP}{$ip_src}++; $CountMaster{IP}{$ip_src}++; ### Generate unique IP id (not just the ident) $ip_id = &Generate_IP_ID($ip_src,$ip_dest,$ip_ident); # # Store IP data in %IP so we can do frag reassembly next # if (! defined $IP{id}{$ip_id}{StartTime}) { $IP{time}{$packet_timefull}{ver} = $ip_ver; $IP{time}{$packet_timefull}{src} = $ip_src; $IP{time}{$packet_timefull}{dest} = $ip_dest; $IP{time}{$packet_timefull}{protocol} = $ip_protocol; $IP{time}{$packet_timefull}{frag}{$ip_frag} = $ip_data; if ($snoop_drops || $tcpdump_drops) { $IP{time}{$packet_timefull}{drops} = 1; } # # If there are more fragments, remember this starttime # unless (($ip_MF == 0) && ($ip_frag == 0)) { $IP{id}{$ip_id}{StartTime} = $packet_timefull; } if (($ip_MF == 1) || ($ip_frag > 0)) { $IP{time}{$packet_timefull}{fragged} = 1; } } else { $start_time = $IP{id}{$ip_id}{StartTime}; $IP{time}{$start_time}{frag}{$ip_frag} = $ip_data; if ($snoop_drops || $tcpdump_drops) { $IP{time}{$packet_timefull}{drops} = 1; } if ($ip_MF == 0) { # # Comlpete this IP packet. This assumes that the # last frag arrives last. # undef $IP{ident}{StartTime}{$ip_id}; } } $packet++; } close INFILE; ### Print status summary unless ($Arg{quiet}) { printf("%s %2.0f%% (%d/%d)","\b"x24, 100,$bytes,$SIZE); print "\nReassembling packets,\n"; } ################################################################### # --- Pass #2, Reassemble IP data in %IP; create %TCP and %UDP --- # &Print_Header1() if $Arg{debug}; $packets = $packet; $packet = 0; @Times = sort { $a <=> $b } ( keys(%{$IP{time}}) ); foreach $time (@Times) { ### Print status summary unless ($Arg{quiet}) { if (($packet % 16) == 0) { printf("%s %2.0f%% (%d/%d)","\b"x32, (100*$packet/$packets),$packet,$packets); } } # # Get IP data from %IP # $ip_ver = $IP{time}{$time}{ver}; $ip_src = $IP{time}{$time}{src}; $ip_dest = $IP{time}{$time}{dest}; $ip_protocol = $IP{time}{$time}{protocol}; $drops = $IP{time}{$time}{drops}; undef $ip_data; # # Reassemble IP frags # if (defined $IP{time}{$time}{fragged}) { @IP_Frags = sort {$a <=> $b} (keys(%{$IP{time}{$time}{frag}})); ### If never recieved the start of the packet, skip if ($IP_Frags[0] != 0) { next; } foreach $ip_frag (@IP_Frags) { $ip_data .= $IP{time}{$time}{frag}{$ip_frag}; } } else { $ip_data = $IP{time}{$time}{frag}{0}; } $length = length($ip_data); # # --- UDP --- # if ($ip_protocol == 17 && $Arg{output_UDP}) { &Process_UDP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops); } # # --- TCP --- # if ($ip_protocol == 6 && $Arg{output_TCP}) { &Process_TCP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops); } # # --- ICMP --- # if ($ip_protocol == 1 && $Arg{output_ICMP}) { &Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops, "ICMP"); } # # --- ICMPv6 --- # if ($ip_protocol == 58 && $Arg{output_ICMP}) { &Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops, "ICMPv6"); } # # Skip packet if it isn't TCP (protocol = 6). (Will add routines for # ICMP, ARP, RARP later on)... # $packet++; ### Memory Cleanup delete $IP{time}{$time}; } ### Memory Cleanup undef %IP; ### Print status summary unless ($Arg{quiet}) { printf("%s %2.0f%% (%d/%d)\n","\b"x24, 100,$packet,$packets); } $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Read Input File - end"; } # Process_TCP_Packet - process a TCP packet and store it in memory. It takes # the raw ip data and populates the data structure %TCP. (and %Count). # sub Process_TCP_Packet { my $ip_data = shift; my $ip_src = shift; my $ip_dest = shift; my $time = shift; my $drops = shift; my $copy; #------------------------------------------------------------------- # # TCP # ### Unpack TCP data ($tcp_src_port,$tcp_dest_port,$tcp_seq,$tcp_ack,$tcp_offset,$tcp_flags, $tcp_header_rest,$tcp_data) = unpack('nnNNCCa6a*',$ip_data); ### Strip off TCP options, if present $tcp_offset = $tcp_offset >> 4; # chuck out reserved bits $tcp_offset = $tcp_offset << 2; # now times by 4 $tcp_options_num = $tcp_offset - 20; if ($tcp_options_num > 0) { ($tcp_options,$tcp_data) = unpack("a${tcp_options_num}a*",$tcp_data); } ### Fetch length and FIN,RST flags $tcp_length_data = length($tcp_data); $tcp_fin = $tcp_flags & 1; $tcp_syn = $tcp_flags & 2; $tcp_rst = $tcp_flags & 4; $tcp_ack = $tcp_flags & 16; $copy = $tcp_data; # # Generate $session_id as a unique id for this stream # (this is built from host:port,host:port - sorting on port). # ($session_id,$from_server) = &Generate_SessionID($ip_src,$tcp_src_port, $ip_dest,$tcp_dest_port,"TCP"); ### Record direction if single SYN was seen if ($tcp_syn && ! $tcp_ack) { $TCP{id}{$session_id}{source} = $ip_src; # better repeat this, ($session_id,$from_server) = &Generate_SessionID($ip_src, $tcp_src_port,$ip_dest,$tcp_dest_port,"TCP"); } ### Count TCP Ports seen if ($from_server) { $Count{TCPport}{$tcp_src_port}++; $CountMaster{TCPport}{$tcp_src_port}++; } else { $Count{TCPport}{$tcp_dest_port}++; $CountMaster{TCPport}{$tcp_dest_port}++; } # # Flag this session as a Partial if either tcpdump or snoop # confesses to dropping packets. # $TCP{id}{$session_id}{Partial}++ if $drops; ### Store size $TCP{id}{$session_id}{size} += length($tcp_data); ### Store the packet timestamp for the first seen packet if (! defined $TCP{id}{$session_id}{StartTime}) { $TCP{id}{$session_id}{StartTime} = $time; ### Store other info once if ($from_server) { $TCP{id}{$session_id}{src} = $ip_dest; $TCP{id}{$session_id}{dest} = $ip_src; $TCP{id}{$session_id}{src_port} = $tcp_dest_port; $TCP{id}{$session_id}{dest_port} = $tcp_src_port; } else { $TCP{id}{$session_id}{src} = $ip_src; $TCP{id}{$session_id}{dest} = $ip_dest; $TCP{id}{$session_id}{src_port} = $tcp_src_port; $TCP{id}{$session_id}{dest_port} = $tcp_dest_port; } } ### Store the packet timestamp in case this is the last packet $TCP{id}{$session_id}{EndTime} = $time; ### Print status line printf "%6s %-45s %s\n",$packet,$session_id,$length if $Arg{debug}; # # --- Store Session Data in Memory --- # # Since TCP is usually the bulk of the data, we minimise # the number of copies of data in memory. UDP and ICMP # are handled differently. if ($from_server) { # # Populate %TCP{id}{}{time} with raw traffic by time. # This is the master structure to store the data. # $TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data; $TCP{id}{$session_id}{time}{$time}{dir} .= "A"; # # # Populate %TCP{id}{}{Aseq} with server to client # 1-way raw traffic, with the TCP sequence number as # the key (for future reassembly). # # This is a pointer to the time structure above, # to save on memory used (originally stored a # duplicate copy of the data). # if ((! defined $TCP{id}{$session_id}{Aseq}{$tcp_seq}) || (length(${$TCP{id}{$session_id}{Aseq}{$tcp_seq}}) < length($tcp_data))) { $TCP{id}{$session_id}{Aseq}{$tcp_seq} = \$TCP{id}{$session_id}{time}{$time}{data}; } # # Populate %Hex{TCP}{} with coloured HTML 2-way # traffic, if needed. # if ($Arg{output_hex}) { &Process_Hex("TCP",$session_id,$tcp_data,"blue"); } } else { # # Populate %TCP{id}{}{Btime} with raw 1-way traffic by time. # This is the master structure to store the data. # $TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data; $TCP{id}{$session_id}{time}{$time}{dir} .= "B"; # # # Populate %TCP{id}{}{Bseq} with client to server # 1-way raw traffic, with the TCP sequence number as # the key (for future reassembly). # # This is a pointer to the time structure above, # to save on memory used (originally stored a # duplicate copy of the data). # if ((! defined $TCP{id}{$session_id}{Bseq}{$tcp_seq}) || (length(${$TCP{id}{$session_id}{Bseq}{$tcp_seq}}) < length($tcp_data))) { $TCP{id}{$session_id}{Bseq}{$tcp_seq} = \$TCP{id}{$session_id}{time}{$time}{data}; } # # Populate %Hex{TCP}{} with coloured HTML 2-way # traffic, if needed. # if ($Arg{output_hex}) { &Process_Hex("TCP",$session_id,$tcp_data,"red"); } } } # Process_UDP_Packet - process a UDP packet and store it in memory. It takes # the raw ip data and populates the data structure %UDP. # sub Process_UDP_Packet { my $ip_data = shift; my $ip_src = shift; my $ip_dest = shift; my $time = shift; my $drops = shift; my $copy; #------------------------------------------------------------------- # # UDP # ### Unpack UDP data ($udp_src_port,$udp_dest_port,$udp_length,$udp_checksum, $udp_data) = unpack('nnnna*',$ip_data); # # Generate $session_id as a unique id for this stream # (this is built from host:port,host:port - sorting on port). # ($session_id,$from_server) = &Generate_SessionID($ip_src,$udp_src_port, $ip_dest,$udp_dest_port,"UDP"); # # Flag this session as a Partial if either tcpdump or snoop # confesses to dropping packets. # $UDP{id}{$session_id}{Partial}++ if $drops; ### Store size $UDP{id}{$session_id}{size} += length($udp_data); ### Count UDP ports seen if ($from_server) { $Count{UDPport}{$udp_src_port}++; $CountMaster{UDPport}{$udp_src_port}++; } else { $Count{UDPport}{$udp_dest_port}++; $CountMaster{UDPport}{$udp_dest_port}++; } # # --- Store Stream Data in Memory --- # if ($from_server) { # # Populate %UDP{id}{}{RawA} with server to client # 1-way raw traffic # $UDP{id}{$session_id}{RawA} .= $udp_data; # # Populate %UDP{id}{}{BothHTML} with coloured HTML # 2-way traffic, blue for server to client # $copy = &Desex_HTML($udp_data); $UDP{id}{$session_id}{BothHTML} .= "$copy"; # # Populate %Hex{UDP}{} with coloured HTML 2-way # traffic, if needed. # if ($Arg{output_hex}) { &Process_Hex("UDP",$session_id,$udp_data,"blue"); } } else { # # Populate %UDP{id}{}{RawB} with client to server # 1-way raw traffic # $UDP{id}{$session_id}{RawB} .= $udp_data; # # Populate %UDP{id}{}{BothHTML} with coloured HTML # 2-way traffic, red for client to server # $copy = &Desex_HTML($udp_data); $UDP{id}{$session_id}{BothHTML} .= "$copy"; # # Populate %Hex{UDP}{} with coloured HTML 2-way # traffic, if needed. # if ($Arg{output_hex}) { &Process_Hex("UDP",$session_id,$udp_data,"red"); } } # # Populate %UDP{id}{}{time}{} with raw 1-way traffic by time # $UDP{id}{$session_id}{time}{$time} .= $udp_data; ### Store the packet timestamp for the first seen packet if (! defined $UDP{id}{$session_id}{StartTime}) { $UDP{id}{$session_id}{StartTime} = $time; ### Store other info once if ($from_server) { $UDP{id}{$session_id}{src} = $ip_dest; $UDP{id}{$session_id}{dest} = $ip_src; $UDP{id}{$session_id}{src_port} = $udp_dest_port; $UDP{id}{$session_id}{dest_port} = $udp_src_port; } else { $UDP{id}{$session_id}{src} = $ip_src; $UDP{id}{$session_id}{dest} = $ip_dest; $UDP{id}{$session_id}{src_port} = $udp_src_port; $UDP{id}{$session_id}{dest_port} = $udp_dest_port; } } ### Store the packet timestamp in case this is the last packet $UDP{id}{$session_id}{EndTime} = $time; ### Print status line printf "%6s %-45s %s\n",$packet,$session_id,$length if $Arg{debug}; } # Process_ICMP_Packet - process a ICMP packet and store it in memory. It takes # the raw ip data and populates the data structure %ICMP. # time is the session_id. # sub Process_ICMP_Packet { my $ip_data = shift; my $ip_src = shift; my $ip_dest = shift; my $time = shift; my $drops = shift; my $ver = shift; #------------------------------------------------------------------- # # ICMP # ### Unpack ICMP data ($icmp_type,$icmp_code,$icmp_cksum,$icmp_rest) = unpack('CCna*',$ip_data); # # --- Store ICMP data in memory --- # ### Store Fields $ICMP{time}{$time}{type} = $icmp_type; $ICMP{time}{$time}{code} = $icmp_code; $ICMP{time}{$time}{src} = $ip_src; $ICMP{time}{$time}{dest} = $ip_dest; $ICMP{time}{$time}{ver} = $ver; # # Flag this session as a Partial if either tcpdump or snoop # confesses to dropping packets. # $ICMP{time}{$time}{Partial}++ if $drops; # # Save data if ICMP echo/reply # if (($icmp_type == 0) || ($icmp_type == 8) || ($icmp_type == 128) || ($icmp_type == 129) || 1) { ### Unpack some more ($icmp_type,$icmp_code,$icmp_cksum,$icmp_id,$icmp_seq, $icmp_data) = unpack('CCnnna*',$ip_data); ### Save extra fields $ICMP{time}{$time}{id} = $icmp_id; $ICMP{time}{$time}{seq} = $icmp_seq; $ICMP{time}{$time}{data} = $icmp_data; } ### Store size $ICMP{time}{$time}{size} += length($icmp_data); if ($icmp_data ne "") { # # Populate %ICMP{time}{}{BothHTML} with coloured HTML # 1-way traffic, blue # $copy = &Desex_HTML($icmp_data); $ICMP{time}{$time}{BothHTML} .= "$copy"; } # # Populate %Hex{ICMP}{} with coloured HTML # traffic, if needed. # if ($Arg{output_hex}) { &Process_Hex("ICMP",$time,$icmp_data,"blue"); } ### Print status line printf "%6s %-45s %s\n",$packet,"$ip_src,$ip_dest",$length if $Arg{debug}; } # Process_TCP_Sessions - this subroutine processes %TCP, saving the # sessions to various "session*" files on disk. It populates %Index # with information on files that it has created. It also checks # the application port numbers and triggers further processing - # eg telnet replay files. Min/Max size checks are also done here. # sub Process_TCP_Sessions { my ($filename,$id_text,$id_html,$rawboth,$time,$raw); my @Time; $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process TCP Sessions - start"; # # Loop through all TCP sessions # foreach $session_id (keys %{$TCP{id}}) { $number = $Index{Sort_Lookup}{"TCP:$session_id"}; # # Determine the service - usually by the lowest numbered port, eg, # ports 51321 and 23 would give 23 (telnet). # $ip_src = $TCP{id}{$session_id}{src}; $ip_dest = $TCP{id}{$session_id}{dest}; $tcp_src_port = $TCP{id}{$session_id}{src_port}; $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; ($service,$client) = &Pick_Service_Port("TCP",$session_id, $tcp_src_port,$tcp_dest_port); ### Fetch text name for this port $service_name = $Services_TCP{$service} || $service || "0"; # # Don't actually save any files if CLI args say not to # if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; } if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; } if ($Arg{ip_reject}) { if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) { next; } } if ($Arg{ip_accept}) { unless ($Arg{IP_Accepted}{$ip_src} || $Arg{IP_Accepted}{$ip_dest}) { next; } } # # --- Fetch RawBoth --- # # rawboth will contain the raw data in time order. $rawboth = ""; foreach $time (sort {$a <=> $b} (keys (%{$TCP{id}{$session_id}{time}}))) { $rawboth .= $TCP{id}{$session_id}{time}{$time}{data}; } $length = length($rawboth); # # --- Check for Min and Max size --- # next if $length < $Arg{minbytes}; next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); ### Print status line $numtext = sprintf("%04d",$number); printf "%6s %-45s %s\n",$numtext,$session_id,$service_name unless $Arg{quiet}; # # --- Save Info File to Disk --- # if ($Arg{output_info}) { $filename = "session_${numtext}.info"; $firsttime = localtime($TCP{id}{$session_id}{StartTime}); $lasttime = localtime($TCP{id}{$session_id}{EndTime}); $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); if ($TCP{id}{$session_id}{Partial}) { $partial = "yes"; } else { $partial = "no"; } ### Build output text $outtext = "$numtext===$session_id===$service===" . "$service_name===$length\n\n" . "Source addr : $ip_src\n" . "Source port : $tcp_src_port\n" . "Dest addr : $ip_dest\n" . "Dest port : $tcp_dest_port\n" . "Dest service: $service_name\n" . "Length bytes: $length\n" . "First time : $firsttime\n" . "Last time : $lasttime\n" . "Duration : $duration seconds\n" . "Partial : $partial\n"; ### Write info file open (OUT,">$filename") || die "ERROR11: creating $filename $!\n"; print OUT $outtext; close OUT; } # # --- Save Index data to Memory --- # ## Fetch times $starttime = scalar localtime($TCP{id}{$session_id}{StartTime}); $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); ### Generate session strings ($id_text,$id_html) = &Generate_TCP_IDs($session_id); ### Construct HTML table row containing session data $Index{HTML}[$number] = "$number." . "$starttime$duration s " . "$id_html " . " " . "$service_name " . "$length bytes\n"; ### Construct text line containing session data $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, $id_text,"($service_name)",$length); ### Construct image info line (in case it is needed) $Image{HTML}[$number]{info} = "$number." . "$starttime " . "$id_html \n"; ### Construct GETPOST info line (in case it is needed) # starttime and host:port... are formatted differently so that # they are narrow and leave more room for the sub table. $GETPOST{HTML}[$number]{info} = "$number." . "$starttime " . "$id_html \n"; # # --- Save Raw Sessions to Disk --- # if ($Arg{output_raw}) { # # Save ".raw" file, all raw 2-way data time-sorted. # $filename = "session_${numtext}.${service_name}.raw"; open (OUT,">$filename") || die "ERROR12: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT $rawboth; close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "
  • raw "; # # Save ".raw1" file, server->client 1-way data assembled. # $filename = "session_${numtext}.${service_name}.raw1"; open (OUT,">$filename") || die "ERROR13: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT &TCP_Follow_RawA($session_id); close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "raw1 "; # # Save ".raw2" file, client->server 1-way data assembled. # $filename = "session_${numtext}.${service_name}.raw2"; open (OUT,">$filename") || die "ERROR14: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT &TCP_Follow_RawB($session_id); close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "raw2
  • "; } next unless $Arg{output_apps}; # # --- Save Session as HTML --- # if ($Arg{Save_As_TCP_HTML}{$service} || $Arg{output_allhtml}) { &Save_Both_HTML("TCP",$session_id,$number,$service_name, $id_html); } # # --- Save X11 Session as HTML --- # if ($Arg{Save_As_X11_HTML}{$service}) { # # HTML Postprocessing can go here # &Generate_X11_HTML($session_id); &Process_BothHTML("TCP",$session_id,1); &Save_Both_HTML("TCP",$session_id,$number,"text$service_name", $id_html); } # # --- Save Hex Dump as HTML --- # if ($Arg{output_hex}) { &Process_Hex_Finish("TCP",$session_id); &Save_Hex_HTML("TCP",$session_id,$number,$service_name, $id_html); &Save_Hex_Text("TCP",$session_id,$number,$service_name, $id_text); } # # --- Process Application Data --- # if ($service == 20) { &Save_FTP_File($session_id,$number); } if ($service == 22) { &Save_Session_textSSH_files($session_id,$number, "SSH",$id_html); } if ($Arg{keydata} && $Arg{Save_As_TCP_Playback}{$service}) { # The following is for special analysis, &Save_Session_Keydata($session_id,$number, $service_name,$id_html); } if ($service == 25) { &Save_SMTP_Emails($session_id,$number); } if ($service == 80 or $service == 8080 or $service == 3127 or $service == 1080) { &Save_HTTP_Files($session_id,$number,$service_name); &Process_HTTP($session_id); } if ($Arg{Save_As_X11_Playback}{$service}) { &Save_Session_XReplay($session_id,$number,$service_name); } if ($Arg{Save_As_VNC_Playback}{$service}) { &Save_Session_VNCReplay_andHTML($session_id,$number, $service_name,$id_html); } $raw = &TCP_Follow_RawB($session_id); if ($raw =~ /^\200\0\0p0\211/) { &Save_NFS_File($session_id,$number); } if ($Arg{Save_As_TCP_Playback}{$service}) { &Save_Session_Replay($session_id,$number,$service_name); } } $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process TCP Sessions - end"; } # Process_UDP_Streams - this subroutine processes %UDP, saving the # sessions to various "session*" files on disk. It populates %Index # with information on the files that were created. It also checks # the application port numbers and triggers further processing - # eg DNS html output files. # sub Process_UDP_Streams { my ($filename,$id_html,$id_text,$time,$rawboth); $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process UDP Sessions - start"; # # Loop through all UDP Streams # foreach $session_id (keys %{$UDP{id}}) { $number = $Index{Sort_Lookup}{"UDP:$session_id"}; # # Determine the service - usually by the lowest numbered port, eg, # ports 51327 and 53 would give 53 (dns). (big assumption!) # $ip_src = $UDP{id}{$session_id}{src}; $ip_dest = $UDP{id}{$session_id}{dest}; $udp_src_port = $UDP{id}{$session_id}{src_port}; $udp_dest_port = $UDP{id}{$session_id}{dest_port}; ($service,$client) = &Pick_Service_Port("UDP",$session_id, $udp_src_port,$udp_dest_port); ### Fetch text name for this port $service_name = $Services_UDP{$service} || $service || "0"; # # Don't actually save any files if CLI args say not to # if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; } if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; } if ($Arg{ip_reject}) { if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) { next; } } if ($Arg{ip_accept}) { unless ($Arg{IP_Accepted}{$ip_src} || $Arg{IP_Accepted}{$ip_dest}) { next; } } # # --- Fetch RawBoth --- # # rawboth will contain the raw data in time order. $rawboth = ""; foreach $time (sort {$a <=> $b} (keys (%{$UDP{id}{$session_id}{time}}))) { $rawboth .= $UDP{id}{$session_id}{time}{$time}; } $length = length($rawboth); # # --- Check for Min and Max Size --- # next if $length < $Arg{minbytes}; next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); ### Print status line $numtext = sprintf("%04d",$number); printf "%6s %-45s %s\n",$numtext,$session_id,$service_name unless $Arg{quiet}; # # --- Save Info File to Disk --- # if ($Arg{output_info}) { $filename = "stream_${numtext}.info"; $firsttime = localtime($UDP{id}{$session_id}{StartTime}); $lasttime = localtime($UDP{id}{$session_id}{EndTime}); $duration = ($UDP{id}{$session_id}{EndTime} - $UDP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); if ($UDP{id}{$session_id}{Partial}) { $partial = "yes"; } else { $partial = "no"; } ### Build output text $outtext = "$numtext===$session_id===$service===" . "$service_name===$length\n\n" . "Source addr : $ip_src\n" . "Source port : $udp_src_port\n" . "Dest addr : $ip_dest\n" . "Dest port : $udp_dest_port\n" . "Dest service: $service_name\n" . "Length bytes: $length\n" . "First time : $firsttime\n" . "Last time : $lasttime\n" . "Duration : $duration seconds\n" . "Partial : $partial\n"; ### Write info file open (OUT,">$filename") || die "ERROR15: creating $filename $!\n"; print OUT $outtext; close OUT; } # # --- Save Index data in Memory --- # ### Fetch Times $starttime = scalar localtime($UDP{id}{$session_id}{StartTime}); $duration = ($UDP{id}{$session_id}{EndTime} - $UDP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); ### Construct HTML table row containing stream data $id_html = "$ip_src:$udp_src_port <-> $ip_dest:$udp_dest_port"; $Index{HTML}[$number] = "$number." . "$starttime$duration s " . "$id_html " . " " . "$service_name " . "$length bytes\n"; ### Construct text line containing session data $id_text = "$ip_src:$udp_src_port <-> $ip_dest:$udp_dest_port"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, $id_text,"($service_name)",$length); # # --- Save Raw Stream to Disk --- # if ($Arg{output_raw}) { # # Save ".raw" file, all raw 2-way data time-sorted. # $filename = "stream_${numtext}.${service_name}.raw"; open (OUT,">$filename") || die "ERROR16: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT $rawboth; close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "
  • raw "; # # Save ".raw1" file, server->client 1-way data time-sorted. # $filename = "stream_${numtext}.${service_name}.raw1"; open (OUT,">$filename") || die "ERROR17: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT $UDP{id}{$session_id}{RawA}; close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "raw1 "; # # Save ".raw2" file, client->server 1-way data time-sorted. # $filename = "stream_${numtext}.${service_name}.raw2"; open (OUT,">$filename") || die "ERROR18: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT $UDP{id}{$session_id}{RawB}; close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "raw2
  • "; } next unless $Arg{output_apps}; # # --- Save Stream as HTML --- # if ($Arg{Save_As_UDP_HTML}{$service} || $Arg{output_allhtml}) { # # HTML Postprocessing can go here # &Process_BothHTML("UDP",$session_id); &Save_Both_HTML("UDP",$session_id,$number,$service_name); } # # --- Save Hex Dump as HTML --- # if ($Arg{output_hex}) { &Process_Hex_Finish("UDP",$session_id); &Save_Hex_HTML("UDP",$session_id,$number,$service_name, $id_html); &Save_Hex_Text("UDP",$session_id,$number,$service_name, $id_text); } # # --- Process Application Data --- # if ($Arg{Save_As_UDP_Playback}{$service}) { &Save_Stream_Replay($session_id,$number,$service_name); } } $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process UDP Sessions - end"; } # Process_ICMP - this subroutine processes %ICMP. # sub Process_ICMP { my ($filename,$id_text,$id_html); $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process ICMP Sessions - start"; # # Loop through all ICMP Streams # foreach $time (keys %{$ICMP{time}}) { $number = $Index{Sort_Lookup}{"ICMP:$time"}; ### Fetch Data $icmp_type = $ICMP{time}{$time}{type}; $icmp_code = $ICMP{time}{$time}{code}; $icmp_ver = $ICMP{time}{$time}{ver}; $ip_src = $ICMP{time}{$time}{src}; $ip_dest = $ICMP{time}{$time}{dest}; $session_id = "$ip_src,$ip_dest"; ### Fetch text name for this port $type_name = $ICMP_Types{$icmp_type} || $icmp_type || "0"; $service_name = $icmp_type; # # Don't actually save any files if CLI args say not to # if ($Arg{ip_reject}) { if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}){ next; } } if ($Arg{ip_accept}) { unless ($Arg{IP_Accepted}{$ip_src} || $Arg{IP_Accepted}{$ip_dest}) { next; } } # # --- Check for Min and Max Size --- # $length = length($ICMP{time}{$time}{data}); next if $length < $Arg{minbytes}; next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); ### Print status line $numtext = sprintf("%04d",$number); printf "%6s %-45s ICMP %s\n",$numtext,$session_id,$type_name unless $Arg{quiet}; # # --- Save Info File to Disk --- # if (($Arg{output_info}) && ($length > 0)) { $filename = "icmp_${numtext}.${service_name}.info"; if ($ICMP{time}{$time}{Partial}) { $partial = "yes"; } else { $partial = "no"; } $starttime = scalar localtime($time); ### Build output text $outtext = "$numtext===$session_id===$icmp_type===" . "$type_name===$length\n\n" . "Source addr : $ip_src\n" . "Dest addr : $ip_dest\n" . "ICMP version: $icmp_ver\n" . "ICMP type : $icmp_type\n" . "ICMP code : $icmp_code\n" . "ICMP name : $type_name\n" . "Length bytes: $length\n" . "Time : $starttime\n" . "Partial : $partial\n"; ### Write info file open (OUT,">$filename") || die "ERROR19: creating $filename $!\n"; print OUT $outtext; close OUT; } # # --- Save Index data in Memory --- # ### Fetch Times $starttime = scalar localtime($time); ### Construct HTML table row containing stream data $id_html = "$ip_src -> $ip_dest"; $Index{HTML}[$number] = "$number." . "$starttime0 s " . "$id_html" . " " . "$icmp_ver " . "$length bytes$type_name\n"; ### Construct text line containing session data $id_text = "$ip_src -> $ip_dest"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, $id_text, "($icmp_ver $type_name)",$length); # # --- Save Raw Stream to Disk --- # if (($Arg{output_raw}) && ($length > 0)) { # # Save ".raw" file, all raw 2-way data time-sorted. # $filename = "icmp_${numtext}.${service_name}.raw"; open (OUT,">$filename") || die "ERROR20: creating $filename $!\n"; binmode(OUT); # for backward OSs print OUT $ICMP{time}{$time}{data}; close OUT; ### Update HTML index table with link $Index{HTML}[$number] .= "
  • raw "; } # # --- Save Stream as HTML --- # if ($Arg{output_allhtml}) { # # HTML Postprocessing can go here # &Process_BothHTML("ICMP",$time); &Save_Both_HTML("ICMP",$time,$number,$service_name,$id_html); } # # --- Save Hex Dump as HTML --- # if ($Arg{output_hex}) { &Process_Hex_Finish("ICMP",$time); &Save_Hex_HTML("ICMP",$time,$number,$service_name,$id_html); &Save_Hex_Text("ICMP",$time,$number,$service_name,$id_text); } } $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; $Bench{$BM}{text} = "Process ICMP Sessions - end"; } # Process_HTTP - HTTP processing. Looks for GETs and POSTs, and process them # into %GETPOST. Constructs a HTTP log in %HTTPlog. # sub Process_HTTP { my ($junk,$var,$value,$term,$data,$request,$site,$post,$get,$reply); my ($start,$src,$num,$req,$recv,$type,$status,$time1,$duration,$dest); my @Terms; my $index = 0; my $indexA = 0; my $indexB = 0; ### Input my $session_id = shift; $src = $TCP{id}{$session_id}{src}; $dest = $TCP{id}{$session_id}{dest}; # # Process # ### Get packet times (may need to use seqs instead) @Times = sort{$a <=> $b} (keys(%{$TCP{id}{$session_id}{time}})); ### Step through each packet for ($i=0; $i <= $#Times; $i++) { ### Fetch data from mem $time = $Times[$i]; $request = $TCP{id}{$session_id}{time}{$time}{data}; $request =~ s/^\0\0*//; # # --- Do HTTPlog Processing --- # next unless $request =~ /^(GET|POST)\s/; # speed ### Calc duration $time1 = $Times[$i+1] || $time; $duration = $time1 - $time; # some magic $reply = ""; foreach $inc (1..16) { $next = $TCP{id}{$session_id}{time}{$Times[$i+$inc]}{data}; $next =~ s/^\0\0*//; if ($next =~ /^U*\0*HTTP/) { $reply = $next; $time1 = $Times[$i+$inc] || $time; $duration = $time1 - $time; last; } else { $request .= $next; } } $i++; # speed if ($request =~ /^GET \S* HTTP/) { ### Get the site string ($site) = $request =~ /^GET (\S*)\s/; if ($site =~ m:^/:) { # assume this was a http, missing the "http://host" $site = "http://${dest}$site"; } ### Get the status and mime type from reply ($status) = $reply =~ /HTTP\/\S*\s(\S*)/s; ($type) = $reply =~ /Content-Type:\s(\S*)/s; ($size) = $reply =~ /Content-Length:\s(\S*)/s; $type = "-" if $type eq ""; $size = 0 if $size eq ""; $result = $Result_Names{$status} || "TCP_HIT"; ### Store the log entry $HTTPlog{time}{$time} = sprintf("%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s\n", int($time),(($time - int($time))*1000),($duration*1000), $src,$result,$status,$size,"GET",$site,"-","NONE","", "-",$type); $HTTPlog{notempty} = 1; } elsif ($request =~ /^POST .* HTTP/) { ### Get the site string ($site) = $request =~ /^POST (\S*)\s/; if ($site =~ m:^/:) { # assume this was a http, missing the "http://host" $site = "http://${dest}$site"; } ### Get the status and mime type ($status) = $reply =~ /HTTP\/\S*\s(\S*)/s; ($type) = $reply =~ /Content-Type:\s(\S*)/s; ($size) = $reply =~ /Content-Length:\s(\S*)/s; $type = "-" if $type eq ""; $size = length($TCP{id}{$session_id}) if $size eq ""; $result = $Result_Names{$status} || "TCP_HIT"; ### Store the log entry $HTTPlog{time}{$time} = sprintf("%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s\n", int($time),(($time - int($time))*1000),($duration*1000), $src,$result,$status,$size,"POST",$site,"-","NONE","", "-",$type); $HTTPlog{notempty} = 1; } # # --- Do GETPOST Processing --- # if ($request =~ /^GET \S*\?\S* HTTP/) { ### Get the GET string ($site,$get) = $request =~ /^GET (\S*)\?(\S*)\s/; # check it looks like a GET, if ($get =~ /=/) { # # Populate %GETPOST with a table containing the GET data # if (! defined $GETPOST{HTML}[$number]{query}) { $GETPOST{HTML}[$number]{info} .= "GET"; $GETPOST{notempty} = 1; } else { $GETPOST{HTML}[$number]{query} .= "
    \n"; } # # Generate table of query key value pairs # $GETPOST{HTML}[$number]{query} .= "$site
    \n"; @Terms = split(/&/,$get); foreach $term (@Terms) { ($var,$value) = split(/=/,$term); $value =~ tr/+/ /; $value =~ s/%([a-f0-9][a-f0-9])/pack("C",hex($1))/egi; $value =~ s//>/g; $value =~ s/\n/
    \n/g; $GETPOST{HTML}[$number]{query} .= "" . "\n"; } $GETPOST{HTML}[$number]{query} .= "
    $var$value
    \n"; } } elsif ($request =~ /^POST .* HTTP/) { ### Get the POST strings ($junk,$post,$junk1) = split(/\n\n|\r\n\r\n/,$request); # check it looks like a POST if ($post =~ /=/) { # # Populate %GETPOST with a table containing the POST data # if (! defined $GETPOST{HTML}[$number]{query}) { $GETPOST{HTML}[$number]{info} .= "POST"; $GETPOST{notempty} = 1; } else { $GETPOST{HTML}[$number]{query} .= "
    \n"; } ($site) = $request =~ /^POST (\S*)\s/; $post =~ s/HTTP .*//s; # # Generate table of query key value pairs # $GETPOST{HTML}[$number]{query} .= "$site
    \n"; @Terms = split(/&/,$post); foreach $term (@Terms) { ($var,$value) = split(/=/,$term); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s//>/g; $value =~ s/\n/
    /g; $GETPOST{HTML}[$number]{query} .= "" . "\n"; } $GETPOST{HTML}[$number]{query} .= "
    $var$value
    \n"; } } } } # Sort_Index - this creates a sort order for the master index.html, based # on the sort argument (defaults to sort by time). # sub Sort_Index { if ($Arg{sort} eq "size") { &Sort_Index_By_Size(); } elsif ($Arg{sort} eq "type") { &Sort_Index_By_Type(); } elsif ($Arg{sort} eq "ip") { &Sort_Index_By_IP(); } else { &Sort_Index_By_Time(); } } # Sort_Index_By_Time - this calculates an appropriate order for the index # files based on session start time. # sub Sort_Index_By_Time { my ($session_id,$time,$number); # # Determine Session and Stream time order # foreach $session_id (keys %{$TCP{id}}) { $Index{Time_Order}{"TCP:$session_id"} = $TCP{id}{$session_id}{StartTime}; } foreach $session_id (keys %{$UDP{id}}) { $Index{Time_Order}{"UDP:$session_id"} = $UDP{id}{$session_id}{StartTime}; } foreach $time (keys %{$ICMP{time}}) { $Index{Time_Order}{"ICMP:$time"} = $time; } $number = 0; foreach $session (sort {$Index{Time_Order}{$a} <=> $Index{Time_Order}{$b}} keys %{$Index{Time_Order}}) { $number++; $Index{Sort_Lookup}{$session} = $number; } } # Sort_Index_By_Size - this calculates an appropriate order for the index # files based on session size. # sub Sort_Index_By_Size { my ($session_id,$time,$number); # # Determine Session and Stream size order # foreach $session_id (keys %{$TCP{id}}) { $Index{Size_Order}{"TCP:$session_id"} = $TCP{id}{$session_id}{size}; } foreach $session_id (keys %{$UDP{id}}) { $Index{Size_Order}{"UDP:$session_id"} = $UDP{id}{$session_id}{size}; } foreach $time (keys %{$ICMP{time}}) { $Index{Size_Order}{"ICMP:$time"} = $ICMP{time}{$time}{size}; } $number = 0; foreach $session (sort {$Index{Size_Order}{$b} <=> $Index{Size_Order}{$a}} keys %{$Index{Size_Order}}) { $number++; $Index{Sort_Lookup}{$session} = $number; } } # Sort_Index_By_Type - this calculates an appropriate order for the index # files based on session type, followed by time. # sub Sort_Index_By_Type { my ($service,$tcp_src_port,$tcp_dest_port,$client,$udp_src_port, $udp_dest_port,$session_id,$time,$number); # # Determine Session and Stream time order # foreach $session_id (keys %{$TCP{id}}) { # Determine the service - usually by the lowest numbered port $tcp_src_port = $TCP{id}{$session_id}{src_port}; $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; ($service,$client) = &Pick_Service_Port("TCP",$session_id, $tcp_src_port,$tcp_dest_port); $Index{Type_Order}{"TCP:$session_id"}{1} = 1; $Index{Type_Order}{"TCP:$session_id"}{2} = $service; $Index{Type_Order}{"TCP:$session_id"}{3} = $TCP{id}{$session_id}{StartTime}; } foreach $session_id (keys %{$UDP{id}}) { # Determine the service - usually by the lowest numbered port $udp_src_port = $UDP{id}{$session_id}{src_port}; $udp_dest_port = $UDP{id}{$session_id}{dest_port}; ($service,$client) = &Pick_Service_Port("UDP",$session_id, $udp_src_port,$udp_dest_port); $Index{Type_Order}{"UDP:$session_id"}{1} = 2; $Index{Type_Order}{"UDP:$session_id"}{2} = $service; $Index{Type_Order}{"UDP:$session_id"}{3} = $UDP{id}{$session_id}{StartTime}; } foreach $time (keys %{$ICMP{time}}) { $Index{Type_Order}{"ICMP:$time"}{1} = 3; $Index{Type_Order}{"ICMP:$time"}{2} = 0; $Index{Type_Order}{"ICMP:$time"}{3} = $time; } # now we sort by TCP->UDP->IP then port then time. $number = 0; foreach $session (sort { $Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} || $Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} || $Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3} } keys %{$Index{Type_Order}}) { $number++; $Index{Sort_Lookup}{$session} = $number; } } # Sort_Index_By_IP - this calculates an appropriate order for the index # files based on client IP, followed by time. # sub Sort_Index_By_IP { my ($service,$ip,$ip_dest,$ip_src,$client, $session_id,$time,$number,$text,$html,$rest); my @IP; # # Determine Session and Stream time order # foreach $session_id (keys %{$TCP{id}}) { # Determine source IP # here we use the same subroutine as the index.html # so that they match up. ($text,$html) = &Generate_TCP_IDs($session_id); ($ip,$rest) = split(/:/,$text,2); # Split on IPv4 or IPv6 $IP = (); if ($ip =~ /\./) { @IP = split(/\./,$ip); } else { $IP[0] = $ip; } $Index{Type_Order}{"TCP:$session_id"}{1} = $IP[0]; $Index{Type_Order}{"TCP:$session_id"}{2} = $IP[1]; $Index{Type_Order}{"TCP:$session_id"}{3} = $IP[2]; $Index{Type_Order}{"TCP:$session_id"}{4} = $IP[3]; $Index{Type_Order}{"TCP:$session_id"}{5} = $TCP{id}{$session_id}{StartTime}; } foreach $session_id (keys %{$UDP{id}}) { # Determine source IP $ip = $UDP{id}{$session_id}{src}; # Split on IPv4 or IPv6 $IP = (); if ($ip =~ /\./) { @IP = split(/\./,$ip); } else { $IP[0] = $ip; } $Index{Type_Order}{"UDP:$session_id"}{1} = $IP[0]; $Index{Type_Order}{"UDP:$session_id"}{2} = $IP[1]; $Index{Type_Order}{"UDP:$session_id"}{3} = $IP[2]; $Index{Type_Order}{"UDP:$session_id"}{4} = $IP[3]; $Index{Type_Order}{"UDP:$session_id"}{5} = $UDP{id}{$session_id}{StartTime}; } foreach $time (keys %{$ICMP{time}}) { # Determine source IP $ip = $ICMP{time}{$time}{src}; # Split on IPv4 or IPv6 $IP = (); if ($ip =~ /\./) { @IP = split(/\./,$ip); } else { $IP[0] = $ip; } $Index{Type_Order}{"ICMP:$time"}{1} = $IP[0]; $Index{Type_Order}{"ICMP:$time"}{2} = $IP[1]; $Index{Type_Order}{"ICMP:$time"}{3} = $IP[2]; $Index{Type_Order}{"ICMP:$time"}{4} = $IP[3]; $Index{Type_Order}{"ICMP:$time"}{5} = $time; } # now we sort by IP then time $number = 0; foreach $session (sort { $Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} || $Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} || $Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3} || $Index{Type_Order}{$a}{4} <=> $Index{Type_Order}{$b}{4} || $Index{Type_Order}{$a}{1} cmp $Index{Type_Order}{$b}{1} || $Index{Type_Order}{$a}{5} <=> $Index{Type_Order}{$b}{5} } keys %{$Index{Type_Order}}) { $number++; $Index{Sort_Lookup}{$session} = $number; } } # Print_Welcome - print short program welcome message. # sub Print_Welcome { unless ($Arg{quiet}) { print "Chaosreader ver 0.94\n\n"; } } # Print_Header1 - print program welcome message. # sub Print_Header1 { unless ($Arg{quiet}) { print "Reading $TYPE log...\n"; printf "%6s %-45s %s\n","Packet", "Session (host:port <=> host:port)","Length"; } } # Print_Header2 - print header before loading the file # sub Print_Header2 { print "\nCreating files...\n" unless $Arg{quiet}; printf "%6s %-45s %s\n","Num","Session (host:port <=> host:port)", "Service" unless $Arg{quiet}; } # Print_Footer1 - print footer at end of program. # sub Print_Footer1 { if ($Arg{output_index}) { print "\nindex.html created.\n" unless $Arg{quiet}; } } # Chdir - change directory with error # sub Chdir { my $dir = shift; # # This can be invoked with $Arg{output_dir}, so $dir won't # always be defined - which is okay. # if (defined $dir) { chdir "$dir" || die "ERROR21: Can't cd to $dir: $!\n"; } } # Create_Index_Files - Create the HTML and text index files. This reads # %Index and creates the files on disk. # sub Create_Index_Files { my ($html_index,$html_line,$html_links,$image_empty,$getpost_empty); $getpost_empty = $image_empty = ""; if ($Arg{output_index}) { ###################### # --- index.html --- $image_empty = "(Empty) " unless $Image{notempty}; $getpost_empty = "(Empty) " unless $GETPOST{notempty}; $httplog_empty = "(Empty) " unless $HTTPlog{notempty}; # # Create HTML Index file containing all reports # open(FILE,">index.html") || die "ERROR22: creating index: $!\n"; print FILE < Chaosreader Report, $Arg{infile} Chaosreader Report
    File: $Arg{infile}, Type: $TYPE, Created at: $the_date

    Image Report $image_empty - Click here for a report on captured images.
    GET/POST Report $getpost_empty - Click here for a report on HTTP GETs and POSTs.
    HTTP Proxy Log $httplog_empty - Click here for a generated proxy style HTTP log.

    TCP/UDP/... Sessions
    END_HTML for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) { $html_line = $Index{HTML}[$html_index]; next unless defined $html_line; print FILE "$html_line \n"; } print FILE <

    IP Count

    END_HTML foreach $IP (sort {$Count{IP}{$b} <=> $Count{IP}{$a}} keys %{$Count{IP}}) { print FILE "\n"; } print FILE <

    TCP Port Count

    $IP$Count{IP}{$IP}
    END_HTML foreach $port (sort {$Count{TCPport}{$b} <=> $Count{TCPport}{$a}} keys %{$Count{TCPport}}) { $port_text = $Services_TCP{$port} || $port || "0"; print FILE "\n"; } print FILE <

    UDP Port Count

    $port_text$Count{TCPport}{$port}" . "
    END_HTML foreach $port (sort {$Count{UDPport}{$b} <=> $Count{UDPport}{$a}} keys %{$Count{UDPport}}) { $port_text = $Services_UDP{$port} || $port || "0"; print FILE "\n"; } print FILE <

    IP Protocol Count

    $port_text$Count{UDPport}{$port}" . "
    END_HTML foreach $protocol (sort {$Count{IPprotocol}{$b} <=> $Count{IPprotocol}{$a}} keys %{$Count{IPprotocol}}) { $protocol_text = $IP_Protocols{$protocol}; print FILE "\n"; } print FILE <

    Ethernet Type Count

    $protocol_text" . "$Count{IPprotocol}{$protocol}
    END_HTML foreach $type (sort {$Count{EtherType}{$b} <=> $Count{EtherType}{$a}} keys %{$Count{EtherType}}) { print FILE "\n"; } print FILE < END_HTML ###################### # --- index.text --- # # Create Text index file # open(FILE,">index.text") || die "ERROR23: creating index: $!\n"; print FILE "TCP/UDP/... Sessions\nFile: $Arg{infile}, " . "Type: $TYPE, Created at: $the_date\n\n"; print FILE @{$Index{Text}}; close FILE; ###################### # --- image.html --- # # Create HTML Image Index file to display images # open(FILE,">image.html") || die "ERROR24: creating index: $!\n"; print FILE < Chaosreader Image Report Chaosreader Image Report
    Created at: $the_date, Type: $TYPE

    Images

    $type$Count{EtherType}{$type}" . "
    END_HTML for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) { $html_line = $Image{HTML}[$html_index]{info}; $html_links = $Image{HTML}[$html_index]{links}; next unless defined $html_links; print FILE "$html_line $html_links \n"; } print FILE <

    END_HTML ###################### # --- getpost.html --- # # Create HTML GETPOST Index file to show HTTP GETs and POSTs # open(FILE,">getpost.html") || die "ERROR25: creating index: $!\n"; print FILE < Chaosreader GET/POST Report Chaosreader GET/POST Report
    Created at: $the_date, Type: $TYPE

    HTTP GETs and POSTs

    END_HTML for ($html_index=0; $html_index <= $#{$GETPOST{HTML}}; $html_index++) { $html_line = $GETPOST{HTML}[$html_index]{info}; $html_links = $GETPOST{HTML}[$html_index]{query}; next unless defined $html_links; print FILE "$html_line $html_links \n"; } print FILE <

    END_HTML } } # Create_Index_Master - Create the HTML and text master index files. This # reads @Master and creates the files on disk. # sub Create_Index_Master { my ($start,$end,$dir,$file,$index,$duration); if ($Arg{output_index}) { # # Create most recent link # $dir = $Master[$#Master]{dir}; $recentname = "most_recent_index"; unlink("$recentname"); # don't die on symlink error, it's not essential symlink("$dir","$recentname"); # # Create HTML Index file containing all reports # open(FILE,">index.html") || die "ERROR26: creating index: $!\n"; print FILE < Chaosreader Master Index Chaosreader Master Index
    Created at: $the_date, Type: $TYPE

    Most Recent Report - Click here for the most recent index, and click reload for updates.

    Chaosreader Reports

    END_HTML for ($index=0; $index <= $#Master; $index++) { $start = $Master[$index]{starttime}; $end = $Master[$index]{endtime}; $dir = $Master[$index]{dir}; $file = $Master[$index]{file}; $size = $Master[$index]{size}; $duration = $Master[$index]{duration}; $html_line = "" . "\n" . "" . "" . "\n"; print FILE "$html_line \n"; } print FILE <

    IP Count

    ". ($index+1) . "$start$end$duration s " . "$size bytes$dir/$file
    END_HTML foreach $IP (sort {$CountMaster{IP}{$b} <=> $CountMaster{IP}{$a}} keys %{$CountMaster{IP}}) { print FILE "\n"; } print FILE <

    TCP Port Count

    $IP$CountMaster{IP}{$IP}" . "
    END_HTML foreach $port (sort {$CountMaster{TCPport}{$b} <=> $CountMaster{TCPport}{$a}} keys %{$CountMaster{TCPport}}) { $port_text = $Services_TCP{$port} || $port || "0"; print FILE "\n"; } print FILE <

    UDP Port Count

    $port_text" . "$CountMaster{TCPport}{$port}
    END_HTML foreach $port (sort {$CountMaster{UDPport}{$b} <=> $CountMaster{UDPport}{$a}} keys %{$CountMaster{UDPport}}) { $port_text = $Services_UDP{$port} || $port || "0"; print FILE "\n"; } print FILE <

    IP Protocol Count

    $port_text" . "$CountMaster{UDPport}{$port}
    END_HTML foreach $protocol (sort {$CountMaster{IPprotocol}{$b} <=> $CountMaster{IPprotocol}{$a}} keys %{$CountMaster{IPprotocol}}) { $protocol_text = $IP_Protocols{$protocol}; print FILE "\n"; } print FILE <

    Ethernet Type Count

    $protocol_text" . "$CountMaster{IPprotocol}{$protocol}
    END_HTML foreach $type (sort {$CountMaster{EtherType}{$b} <=> $CountMaster{EtherType}{$a}} keys %{$CountMaster{EtherType}}) { print FILE "\n"; } print FILE < END_HTML # # Create Text index file # open(FILE,">index.text") || die "ERROR27: creating index: $!\n"; print FILE "Master Indexes\nCreated at: $the_date, Type: $TYPE\n\n"; for ($index=0; $index <= $#Master; $index++) { $start = $Master[$index]{starttime}; $end = $Master[$index]{endtime}; $dir = $Master[$index]{dir}; $file = $Master[$index]{file}; $size = $Master[$index]{size}; $duration = $Master[$index]{duration}; printf FILE "%-25s %3s s %8s b %s\n",$start,$duration, $size,"$dir/index.text"; } close FILE; # # Create index.file for redos # open(FILE,">index.file") || die "ERROR28: creating index: $!\n"; for ($index=0; $index <= $#Master; $index++) { $dir = $Master[$index]{dir}; $file = $Master[$index]{file}; $start = $Master[$index]{starttime}; $end = $Master[$index]{endtime}; $duration = $Master[$index]{duration}; print FILE "$dir\t$file\t$duration\t$start\t$end\n"; } close FILE; } } # Create_Log_Files - create log files such as the HTTP log. # sub Create_Log_Files { #BDG some memory debug #system("pmap -x $$"); # # Create HTTPlog.text # open(FILE,">httplog.text") || die "ERROR29: creating HTTP log: $!\n"; foreach $time (sort { $a <=> $b }(keys (%{$HTTPlog{time}}))) { print FILE $HTTPlog{time}{$time}; } close FILE; } # File_Type - return file extension for given data, else "data". # sub File_Type { my $data = $_[0]; my $type = ""; if ($data =~ /^GIF8[7-9]/) { $type = "gif"; } elsif ($data =~ /^\377.....(JPEG|JFIF)/) { $type = "jpeg"; } elsif ($data =~ /^PK\003\004/) { $type = "zip"; } elsif ($data =~ /^\%PDF/) { $type = "pdf"; } elsif ($data =~ /^\037\213/) { $type = "gz"; } elsif ($data =~ /^BZh/) { $type = "bz2"; } elsif ($data =~ /^\177ELF/) { $type = "elf"; } elsif ($data =~ /^\%!/) { $type = "ps"; } elsif ($data =~ //i) { $type = "html"; } else { $type = "data"; } return $type; } # Is_Image - returns true if extension is for an image. # sub Is_Image { my $ext = shift; return 1 if ($ext eq "jpeg"); return 1 if ($ext eq "gif"); return 0; } # Desex_HTML - Removes HTML tags ("<" and ">") from data, so that it no # longer interferes when printed as HTML. # sub Desex_HTML { ### Input my $data = shift; ### Process # remove "<" and ">"s $data =~ s//>/g; ### Return return $data; } # Process_BothHTML - Process the HTML 2-way session. Remove binary junk # that dosen't render well in a browser. # sub Process_BothHTML { ### Input my $type = shift; my $session_id = shift; my $plain = shift; my $wrapped = ""; my $index = 0; my $counter = 0; my $intag = 0; my ($char,$data); if ($type eq "TCP") { $data = $TCP{id}{$session_id}{BothHTML}; } elsif ($type eq "UDP") { $data = $UDP{id}{$session_id}{BothHTML}; } elsif ($type eq "ICMP") { $data = $ICMP{time}{$session_id}{BothHTML}; } ### Process (order dependant) $data =~ s/font color="red"> \0]{$WRAP})/$&\n/g; } else { # This is a fancy line wrap, a green ">" starts the wrapped lines $data =~ s/([^\n\f<>]{$WRAP})/$&\n><\/font>/g; } ### Save if ($type eq "TCP") { $TCP{id}{$session_id}{BothHTML} = $data; } elsif ($type eq "UDP") { $UDP{id}{$session_id}{BothHTML} = $data; } elsif ($type eq "ICMP") { $ICMP{time}{$session_id}{BothHTML} = $data; } } # Process_This_HTML - Process the HTML 2-way session. Remove binary junk # that dosen't render well in a browser. # sub Process_This_HTML { ### Input my $data = shift; my $plain = shift; my $wrapped = ""; my $index = 0; my $counter = 0; my $intag = 0; my ($char); ### Process (order dependant) $data =~ s/font color="red"> \0]{$WRAP})/$&\n/g; } else { # This is a fancy line wrap, a green ">" starts the wrapped lines $data =~ s/([^\n\f<>]{$WRAP})/$&\n><\/font>/g; } return $data; } # Process_Hex - Create the coloured HTML 2-way hex dump, and a text dump. # For code reuse it uses it's own data structure %Hex. # (Originally used %TCP{id}{$session_id}{hex}). # sub Process_Hex { ### Input my $type = shift; my $session_id = shift; my $data = shift; my $colour = shift; my $pos = $Hex{$type}{$session_id}{pos}; my $offset = $Hex{$type}{$session_id}{offset}; my $hexhtml = $Hex{$type}{$session_id}{hexhtml}; my $viewhtml = $Hex{$type}{$session_id}{viewhtml}; my $hextext = $Hex{$type}{$session_id}{hextext}; my $viewtext = $Hex{$type}{$session_id}{viewtext}; my (@Bytes,$byte,$view,$view2); $pos = 1 unless defined $pos; $offset = 0 unless defined $offset; $hexhtml .= ""; $viewhtml .= ""; ### Process @Bytes = unpack("C*",$data); foreach $byte (@Bytes) { $view = chr($byte); $view =~ tr/\040-\176/./c; $view2 = $view; $view2 =~ s//>/g; $viewhtml .= $view2; $viewtext .= $view; $hexhtml .= sprintf("%2.2x",$byte); $hextext .= sprintf("%2.2x",$byte); $pos++; if ($pos > 16) { ### Save text version $Hex{$type}{$session_id}{text} .= sprintf("%6.08x",$offset) . " $hextext $viewtext\n"; ### Save HTML version $Hex{$type}{$session_id}{HTML} .= '' . sprintf("%6.08x",$offset) . " $hexhtml $viewhtml\n"; $pos = 1; $offset += 16; $hexhtml = ""; $viewhtml = ""; $hextext = $viewtext = ""; } if ( ($pos != 1) && (($pos %2) == 1) ) { $hexhtml .= " "; $hextext .= " "; } } $hexhtml .= ""; $viewhtml .= ""; $Hex{$type}{$session_id}{pos} = $pos; $Hex{$type}{$session_id}{offset} = $offset; $Hex{$type}{$session_id}{hexhtml} = $hexhtml; $Hex{$type}{$session_id}{viewhtml} = $viewhtml; $Hex{$type}{$session_id}{hextext} = $hextext; $Hex{$type}{$session_id}{viewtext} = $viewtext; } # Process_Hex_Finish - Finish the hex dumps. # sub Process_Hex_Finish { ### Input my $type = shift; my $session_id = shift; my $pos = $Hex{$type}{$session_id}{pos}; my $offset = $Hex{$type}{$session_id}{offset}; my $hexhtml = $Hex{$type}{$session_id}{hexhtml}; my $viewhtml = $Hex{$type}{$session_id}{viewhtml}; my $hextext = $Hex{$type}{$session_id}{hextext}; my $viewtext = $Hex{$type}{$session_id}{viewtext}; my ($short); return unless defined $pos; return if $pos == 1; $short = 39 - length($hextext); $hexhtml .= " " x $short; $hextext .= " " x $short; ### Save text version $Hex{$type}{$session_id}{text} .= sprintf("%6.08x",$offset) . " $hextext $viewtext\n"; ### Save HTML version $Hex{$type}{$session_id}{HTML} .= '' . sprintf("%6.08x",$offset) . " $hexhtml $viewhtml\n"; } # Generate_X11_HTML - fetch the text from an X11 session and save # as bidirectional 2-way coloured HTML. # # Todo: check if a text or keypress event can be split during # transmission and add code similar to X11 replay to handle this. # sub Generate_X11_HTML { my ($filename,$data,$copy,$xcode,$xbyte,$xlength,$xrest,$d, $xlv,$xvalue,$pad,$y,$yold,$chars,$colour,$session_data, $service_name,$colourold,$store,$keytype,$gotsome); my @Times; $session_data = ""; ### Input my $session_id = shift; $data = ""; $service_name = "X11"; ### Processing my $session_text = $session_id; $session_text =~ s/,/ <-> /; ### Fetch raw data $xserver = &TCP_Follow_RawA($session_id); # # Determine endian of this transfer. # ($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver); # # Create aliases for "n" and "N". # if ($xvalue < 256) { $n = "n"; $N = "N"; } else { $n = "v"; $N = "V"; } # # Determine keymap style - see &Set_X11_KeyCodes() # if ($xserver =~ /q...Q.*w...W.*e...E.*r...R.*t...T.*y...Y.*u...U.*i...I.*o...O.*p/) { $keytype = "linux"; } else { $keytype = "sun"; } # # Fetch data from both directions, sorting on timestamps # @Times = sort{$a <=> $b} (keys %{$TCP{id}{$session_id}{time}}); # # --- Main Loop --- # # (this needs to be a for loop!) for ($i=0; $i <= $#Times; $i++) { $time = $Times[$i]; ### Fetch X11 data and direction as a colour if (defined $TCP{id}{$session_id}{time}{$time}{dir}) { $copy = $TCP{id}{$session_id}{time}{$time}{data}; if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { $colour = "red"; } else { $colour = "blue"; } } $xrest = $copy; # # Process through X11 codes # while (length($xrest) > 0) { ### Fetch xcode and other values ($xcode,$xbyte,$xlength,$xrest) = unpack("CC${n}a*",$xrest); $chars = ""; # # Fetch code values from $xrest, and trim # $xrest. For most requests, the value length # is a field (bytes 3,4) except for XErrors # (code 0) where the total length is always 32. # if ($xcode == 0) { $xlv = 28; } else { $xlv = ($xlength - 1) * 4; $xlv = -$xlv if $xlv < 0; } ### Fetch values for this xcode ($xvalue,$xrest) = unpack("a${xlv}a*",$xrest); $store = 0; # # Process a draw text event (76, 77) # if (($colour eq "blue") && (($xcode == 76)||($xcode == 77))) { # Check if this is a xImageText16Req if ($xcode == 77) { $xbyte *= 2; } ($pad,$y,$chars) = unpack("a10${n}a$xbyte",$xvalue); if ($yold != $y) { $chars = "\n$chars"; } $chars =~ s/\0//g; $store = 1; $yold = $y; } # # Process a key pressed event (2) # if (($colour eq "red") && ($xcode = "2")) { ($pad,$caps,$pad) = unpack("a24${n}a*",$xvalue); # # Translate the X11 KeyCode to the actual char # (try "xmodmap -pke") # $chars = $KeyCode{$keytype}{$caps}{$xbyte}; ### Don't keep red \n's for neatness (keep blue ones) unless ($chars eq "\n") { $store = 1; } } # # Process a text scroll event (by using 62 - copy area) # if (($colour eq "blue") && ($xcode == 62)) { $chars = "\n"; $store = 1; } ### Store data if ($store) { if ($colour ne $colourold) { $session_data .= "$chars"; } else { $session_data .= $chars; } $colourold = $colour; } } } $TCP{id}{$session_id}{BothHTML} = $session_data; } # Save_Both_HTML - Save bidirectional (coloured) data into a html file. # sub Save_Both_HTML { my ($filename); ### Input my $type = shift; my $session_id = shift; my $number = shift; my $service_name = shift; my $session_text = shift; my $numtext = sprintf("%04d",$number); my ($base,$raw); $session_text = $session_id unless defined $session_text; ### Processing $session_text =~ s/,/ <-> /; ### Checks $ext = ""; $session_data = ""; if ($type eq "TCP") { $base = "session"; # # Note, the following is similar code for TCP, UDP and ICMP. # However UDP and ICMP use a simple strategy to store and fetch # the processed HTML; whereas TCP uses a complex yet memory # efficient strategy. This is intentional - the way TCP has # been stored has been tuned to reduce memory usage, as TCP has # the bulk of the data (and the bulk of the memory problem). This # has not been necessary with UDP and ICMP (yet). # if ($TCP{id}{$session_id}{BothHTML} ne "") { # # If the BothHTML report has already been calculated, fetch # $session_data = $TCP{id}{$session_id}{BothHTML}; } else { # # Generate a BothHTML report by following packets by time # foreach $time (sort {$a <=> $b} (keys (%{$TCP{id}{$session_id}{time}}))) { $raw = $TCP{id}{$session_id}{time}{$time}{data}; $raw = &Desex_HTML($raw); next unless length($raw); if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { $session_data .= "$raw"; } else { $session_data .= "$raw"; } } $session_data = &Process_This_HTML($session_data); $base = "session"; if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } } } elsif ($type eq "UDP") { $base = "stream"; $session_data = $UDP{id}{$session_id}{BothHTML}; if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } } elsif ($type eq "ICMP") { $base = "icmp"; $session_data = $ICMP{time}{$session_id}{BothHTML}; if ($ICMP{time}{$session_id}{Partial}) { $ext = ".partial"; } } else { $base = "are_belong_to_us"; } ### Do nothing if there is no data ("26" is mostly due to colour tags) return unless ((defined $session_data)&&(length($session_data) > 26)); ### Output $filename = "${base}_${numtext}.${service_name}${ext}.html"; open (OUT,">$filename") || die "ERROR30: file create, $filename: $!\n"; binmode(OUT); print OUT "\n$number" . "\n" . "

    $service_name: $session_text

    \n" . "

    File $Arg{infile}, Session $number

    \n" . "
    \n" .
             $session_data . "
    \n\n\n"; close OUT; ### Global Vars my $length = length($session_data); $Index{HTML}[$number] .= "
  • as_html
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); } # Save_Hex_HTML - Save bidirectional (coloured) hex data into a html file. # sub Save_Hex_HTML { my ($filename); ### Input my $type = shift; my $session_id = shift; my $number = shift; my $service_name = shift; my $session_text = shift; my $session_data = $Hex{$type}{$session_id}{HTML}; my $numtext = sprintf("%04d",$number); my ($base); $session_text = $session_id unless defined $session_text; $session_data = "" unless defined $session_data; ### Processing $session_text =~ s/,/ <-> /; ### Checks $ext = ""; if ($type eq "TCP") { $base = "session"; if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } } elsif ($type eq "UDP") { $base = "stream"; if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } } elsif ($type eq "ICMP") { $base = "icmp"; if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; } } ### Output $filename = "${base}_${numtext}.${service_name}${ext}.hex.html"; open (OUT,">$filename") || die "ERROR31: file create, $filename: $!\n"; binmode(OUT); print OUT "\n$number" . "\n" . "

    $service_name: $session_text

    \n" . "

    File $Arg{infile}, Session $number

    \n" . "
    \n" .
             $session_data . "
    \n\n\n"; close OUT; ### Global Vars my $length = length($session_data); $Index{HTML}[$number] .= "
  • "; $Index{HTML}[$number] .= "hex
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); } # Save_Hex_Text - Save bidirectional hex data into a text file. # sub Save_Hex_Text { my ($filename); ### Input my $type = shift; my $session_id = shift; my $number = shift; my $session_text = shift; my $session_data = $Hex{$type}{$session_id}{text}; my $numtext = sprintf("%04d",$number); my ($base); $session_text = $session_id unless defined $session_text; $session_data = "" unless defined $session_data; ### Processing $session_text =~ s/,/ <-> /; ### Checks $ext = ""; if ($type eq "TCP") { $base = "session"; if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } } elsif ($type eq "UDP") { $base = "stream"; if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } } elsif ($type eq "ICMP") { $base = "icmp"; if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; } } ### Output $filename = "${base}_${numtext}.${service_name}${ext}.hex.text"; open (OUT,">$filename") || die "ERROR32: file create, $filename: $!\n"; binmode(OUT); print OUT "$service_name: $session_text\n" . "File $Arg{infile}, Session $number\n\n$session_data\n"; close OUT; ### Global Vars my $length = length($session_data); $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); } # Save_FTP_File - Save files from an active FTP session. # sub Save_FTP_File { my ($filename,$ftp_data,$length); my $session_id = shift; my $number = shift; my $numtext = sprintf("%04d",$number); my $service_name = "ftp-data"; ### Input $ftp_data = &TCP_Follow_RawB($session_id); if (! defined $ftp_data) { $ftp_data = &TCP_Follow_RawA($session_id); } ### Checks $ftp_type = &File_Type($ftp_data); if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } else { $ext = ""; } ### Output $filename = "session_${numtext}.part_01.$service_name${ext}.$ftp_type"; open (OUT,">$filename") || die "ERROR33: file create, $filename: $!\n"; binmode(OUT); # for backward OSs print OUT $ftp_data; close OUT; ### Global Vars $length = length($ftp_data); $Index{HTML}[$number] .= "
  • $filename $length bytes
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); if (&Is_Image($ftp_type)) { $Image{HTML}[$number]{links} .= " "; $Image{notempty} = 1; } } # NOTE On Replays # # The essence of these is to playback the client/server data so that # the original session can be replayed. There are two styles, # # Text Replays. These playback the text component to the application # data to the screen. These usally work well. The actual text data is not # cleaned up in any way, so to preserve escape sequences necessary to # redisplay in the original style. Eg, telnet. # # GUI Replays, or Server/Client Replays. These often use TCP/IP to send # the data back to the server or client to playback the session. These # are less robust, mainly becuase negotiation can occur slightly differently # causing nothing to be displayed. There is code here to redo the # negotiation - but it is very difficult for this to be 100% robust. # The main reasons the GUI replays fail are colour depth mismatch # and dropped packets. Eg, X11. # # Both styles print the binary data within single quotes ' '. This # creates perl programs that can't be "cat" (use cat -vet), or edited # in vi (use vim) due to the raw binary data. A neater style would be to # translate the binary data into octal or hex text streams, eg # 'print "\015\012\087\012"'... Currently this is not used, as it would # roughly increase the file size by a factor of 4. However plopping # data in the middle of perl programs creates problems of it's own # (see the unusual seds). At some point I may opt for the easier, # although lengthier, method. # Save_Session_Replay - Save a replay program for this session. eg, telnet. # sub Save_Session_Replay { my ($filename,$duration,$time); my $session_id = shift; my $number = shift; my $service_name = shift; my $numtext = sprintf("%04d",$number); ### Output $filename = "session_${numtext}.${service_name}.replay"; $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); open (REPLAY,">$filename") || die "ERROR34: creating $filename $!\n"; binmode(REPLAY); # for backward OSs # # Create a perl program, that when run itself will print out # the contents of the server 1-way stream, with pauses based on # the packet arrival times (replay the session in realtime). # print REPLAY "#!$PERL\n"; print REPLAY <<'END'; # # This is a telnet/login replay program. It will replay a session using # the timestamps from the packet log. # # USAGE: run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.telnet.replay 2 # # Auto generated by Chaosreader. # $| = 1; $factor = $ARGV[0] || 1; sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } END # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { push(@Times,$time) } } @Times = sort { $a <=> $b } @Times; for ($i=0; $i <= $#Times; $i++) { # required ### Calculate time diff if possible if ($i == $#Times) { $timediff = 0; } else { $timediff = $Times[$i+1] - $Times[$i]; if ($timediff < 0) { $timediff = 0; } } $time = $Times[$i]; ### Fetch data from mem $data = $TCP{id}{$session_id}{time}{$time}{data}; # # Clean the data a little (order important) # $data =~ s/\\/\\\\/g; # backslash the backslashes $data =~ s/'/\\'/g; # backslash single quotes # # Now output the data in the replay program # print REPLAY "print '" . $data . "';\n"; # # This causes the replay program to pause # print REPLAY "ms($timediff);\n"; } close REPLAY; ### Better make it executable chmod (0755, "$filename"); ### Global Vars $Index{HTML}[$number] .= "
  • $filename" . " $duration seconds
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename","",$duration); } # Save_Session_textSSH_files - Save a replay program to display the SSH # session in a text format, a html form of this, and a key delay # data file. # # The program "sshkeydata" will take the key delay data file and estimate # the original typed commands. (It also needs a key delay data file # from a plaintext session such as telnet, which is generated by the # Save_Session_Keydata subroutine). # # This has been designed with SSH ver 2 in mind. # sub Save_Session_textSSH_files { my ($filename1,$filename2,$filename3,$duration,$time,$data,$length, $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2, $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize,$datah, $data00); my $session_id = shift; my $number = shift; my $service_name = shift; my $session_text = shift; my $numtext = sprintf("%04d",$number); my $delay = ""; # a text list of key delays my $html = ""; # a html form of output my $bytes = 0; # data bytes of the connection my $minsize; # The min client packet size my $state; $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration2 = sprintf("%.2f",$duration); $duration = sprintf("%.0f",$duration); ### Output $filename1 = "session_${numtext}.text${service_name}.replay"; open (REPLAY,">$filename1") || die "ERROR35: creating $filename1 $!\n"; binmode(REPLAY); # for backward OSs # # Create a perl program that replays details of the original # SSH session. We print the direction of traffic and size, # paused using the original delays. # print REPLAY "#!$PERL\n"; print REPLAY <<'END'; # # This is a text SSH replay program. It will replay details of the # original SSH session using timestamps from the packet capture log. # # USAGE: run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.textSSH.replay 2 # # Auto generated by Chaosreader. # $| = 1; $factor = $ARGV[0] || 1; sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } print <<'SUBEND'; SSH text analysis replay ------------------------ "*" is client traffic (including keystrokes), "." is the return text. A number is a multiple of the previous char, eg ".32" is 32 return chars. SUBEND END # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = (); %PacketSize = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) { next; } push(@Times,$time); if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { ### Frequency count sent sizes $data = $TCP{id}{$session_id}{time}{$time}{data}; $length = length($data); $PacketSize{$length}++ if $length < 100; } } @Times = sort { $a <=> $b } @Times; $outtime = $Times[0]; $outsize = 0; # # Determine the client min size - this is the minimum length of # a data packet, eg a keystroke. # foreach $length (sort {$PacketSize{$b} <=> $PacketSize{$a}} (keys(%PacketSize))) { $minsize = $length; last; } # The very first packet $data00 = $TCP{id}{$session_id}{time}{$Times[0]}{data}; ### Process data for ($i=0; $i <= $#Times; $i++) { # required ### Calculate time diff if possible $time0 = $Times[$i]; $time1 = $Times[$i+1]; $time2 = $Times[$i+2]; $time3 = $Times[$i+3]; if ($i == $#Times) { $timediff1 = 0; $timediff2 = 0; } else { $timediff1 = $time1 - $time0; $timediff2 = $time2 - $time0; if ($timediff1 < 0) { $timediff1 = 0; } } ### Fetch data from mem, "0" is this packet... $data0 = $TCP{id}{$session_id}{time}{$time0}{data}; $data1 = $TCP{id}{$session_id}{time}{$time1}{data}; $data2 = $TCP{id}{$session_id}{time}{$time2}{data}; $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir}; $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir}; $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir}; $dir3 = $TCP{id}{$session_id}{time}{$time3}{dir}; $length0 = length($data0); $length1 = length($data1); $length2 = length($data2); # working variables $bytes += $length0; $length = $length0; $data = $data0; ################## # Process Data # # This is designed for a command line SSH session and # the calculations are based on many assumptions. # # For example: if the client sends a small packet (which # we'll assume is a keystroke) and the server responds # with large packets (beyond merely echoing the keystroke), # then we can assume that this keystroke was the enter key, # and the large response was the output of the command. # # There are two states - keystrokes and output text. # # The follow code works well most of the time, and provides # meaningful results for non command line sessions. # # # --- Server to Client --- # if ($dir0 eq "A") { if ($i > 3 || $data00 !~ /^ssh/i) { # a "." represents an encrypted server to client packet $data = "."; $html .= '' . $data; } else { ### Process initial plaintext negotiation # first we clean up the data, $data =~ tr/\040-\176/./c; $data =~ s/\\/\\\\/g; $data =~ s/'/\\'/g; $data .= "\n"; $hdata = $data; $hdata = &Desex_HTML($hdata); # This is a fancy line wrap, adds a green ">" $hdata =~ s/([^\n\f<>]{$WRAP})/$&\n><\/font>/g; $html .= '' . $hdata; } if ($state eq "output") { if ($length0 > $minsize && $i > 3) { # This prints the length in the replay files # as a number following the symbol, # eg ".60" would mean a "." with length 60. # length actually means size beyond minsize. $length -= $minsize; $data .= "$length"; $html .= "$length"; $outsize += $length; } ### Data -> Keystrokes if ($dir1 eq "B" && $length1 == $minsize) { # Process the transition from command output back # to keystrokes. $data .= "\n"; $html .= "\n"; $delay .= "s $outsize\n"; $delay .= sprintf("t %.6f\n",$time0 - $outtime); $delay .= " \n"; # command delimiter $outsize = 0; $outtime = $time0; $state = "key"; } } $html .= ''; } # # --- Client to Server --- # else { if ($i == 1) { # PuTTY appears to have an unusual way to send keystrokes # to the server, that differs to OpenSSH and Sun's SSH. # Remember if this is a PuTTY session. $sshtype = "putty" if $data =~ /PuTTY/; } ### Keystroke if ($sshtype eq "") { # If the client is sending a minsize packet and the server # then responds, we assume this is a keystroke. if ($length0 == $minsize && $dir1 eq "A") { $delay .= "k \n"; } } elsif ($sshtype eq "putty") { # if the client is sending a minsize packet, followed by # another packet, then a reply packet, and then a server # response; we assume that this is a keystroke. # (This processes PuTTY's doubled keystrokes). if ($length0 == $minsize && $dir1 eq "B" && $dir2 eq "A") { $delay .= "k \n"; } } ### Process initial plaintext negotiation if ($i > 3 || $data00 !~ /^ssh/i) { # a "*" represents an encrypted client to server packet $data = "*"; $html .= '' . $data; } else { ### Process initial plaintext negotiation # first we clean up the data, $data =~ tr/\040-\176/*/c; $data =~ s/\\/\\\\/g; $data =~ s/'/\\'/g; $data .= "\n"; $hdata = $data; $hdata = &Desex_HTML($hdata); # This is a fancy line wrap, adds a green ">" $hdata =~ s/([^\n\f<>]{$WRAP})/$&\n><\/font>/g; $html .= '' . $hdata; } ### Keystroke -> Keystroke delay if ($sshtype eq "") { if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && $length2 == $minsize) { # If this is a keystroke packet, and the next packet # is a response, and then another keystroke packet # is sent; then measure the keystroke delay. $timediff2 = $time2 - $time0; $delay .= sprintf("d %.6f\n",$timediff2); $outsize = 0; $outtime = $time0; } } elsif ($sshtype eq "putty") { if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && $length2 == $minsize && $dir3 eq "B") { # This is the same idea as the above, but processes # PuTTY's doubled keystrokes. $timediff2 = $time2 - $time0; $delay .= sprintf("d %.6f\n",$timediff2); $outsize = 0; $outtime = $time0; } } if ($length0 > $minsize && $i > 3) { # # This prints the length in the replay files # as a number following the symbol, # eg ".60" would mean a "." with length 60. # length actually means size beyond minsize. $length -= $minsize; $data .= "$length"; $html .= "$length"; } $html .= ''; ### Keystrokes -> Data if ( ($length0 == $minsize && (($length1 + $length2) > ($minsize * 2))) || ($dir1 eq "A" && $dir2 eq "A") ) { $data .= "\n"; $html .= "\n"; # # "r" describes the response packet. This value # may or may not be meaningful depending on the # SSH software. if ($length1 > $minsize) { $delay .= "r 1\n"; $delay .= sprintf("p %.6f\n",$timediff1); } else { $delay .= "r 2\n"; $delay .= sprintf("p %.6f\n",$timediff2); } $state = "output"; } } ### Now output the data in the replay program print REPLAY "print '" . $data . "';\n"; ### This causes the replay program to pause print REPLAY "ms($timediff1);\n"; } $speed = sprintf("%.2f",$bytes / (1024 * $duration)); print REPLAY "print \"\n\n" . "Summary: $duration2 seconds, $bytes bytes, $speed Kb/sec\\n\";"; close REPLAY; ### Better make it executable chmod (0755, "$filename1"); # # HTML version of the replay script # $filename2 = "session_${numtext}.text${service_name}.html"; open (HTML,">$filename2") || die "ERROR36: Can't write to file, $filename2 $!\n"; $html = "SSH text analysis\n" . "" . "

    $service_name: $session_text

    \n" . "

    File $Arg{infile}, Session $number

    \n" . "

    $duration2 seconds, $bytes bytes, $speed Kb/sec

    \n" . '"*" is client traffic (including ' . 'keystrokes), "." is the return ' . 'text.
    A number is a multiple of the previous char, eg ".32" ' . 'is 32 return chars.
    ' . "\n
    $html
    \n\n\n"; print HTML $html; close HTML; # # Text Database of time delays between possible keystrokes # $filename3 = "session_${numtext}.text${service_name}.keydata"; open (DELAY,">$filename3") || die "ERROR37: Can't write keydata file: $filename3 $!\n"; $delay = "$delay \n"; print DELAY $delay; close DELAY; # # Update Global Vars to remember new filenames # $Index{HTML}[$number] .= "
  • $filename1" . " $duration seconds
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename1","",$duration); $Index{HTML}[$number] .= "
  • $filename2" . "
  • \n"; $length = length($html); $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename2","",$length); $Index{HTML}[$number] .= "
  • $filename3" . "
  • \n"; $length = length($delay); $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename3","",$length); } # Save_Session_Keydata - Save a key delay data file to assist SSH analysis. # # This code is intentionally designed to be similar to the SSH processing # code, so that both their outputs can be compared. As a standalone # subroutine this wouldn't make too much sense; instead bear in mind that # I'd like the processing to mimic how SSH was processed. That way we # run this on plenty of known text (telnet) and become familiar with # exactly what will happen for the unknown text (SSH). # sub Save_Session_Keydata { my ($filename1,$filename2,$filename3,$duration,$time,$data,$length, $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2, $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize); my $session_id = shift; my $number = shift; my $service_name = shift; my $session_text = shift; my $numtext = sprintf("%04d",$number); my $delay = ""; # a text list of key delays my $minsize; # The min client packet size my $state = "key"; ### Sort the data by timestamps @Times = (); %PacketSize = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) { next; } push(@Times,$time); } @Times = sort { $a <=> $b } @Times; $outtime = $Times[0]; $outsize = 0; $minsize = 1; # known for telnet ### Process data for ($i=0; $i <= $#Times; $i++) { # required ### Calculate time diff if possible $time0 = $Times[$i]; $time1 = $Times[$i+1]; $time2 = $Times[$i+2]; if ($i == $#Times) { $timediff1 = 0; $timediff2 = 0; } else { $timediff1 = $time1 - $time0; $timediff2 = $time2 - $time0; if ($timediff1 < 0) { $timediff1 = 0; } } ### Fetch data from mem, "0" is this packet... $data0 = $TCP{id}{$session_id}{time}{$time0}{data}; $data1 = $TCP{id}{$session_id}{time}{$time1}{data}; $data2 = $TCP{id}{$session_id}{time}{$time2}{data}; $data0 = "\n" if $data0 eq "\r\n"; $data1 = "\n" if $data1 eq "\r\n"; $data2 = "\n" if $data2 eq "\r\n"; $data0 = "\n" if $data0 =~ /\r./; $data1 = "\n" if $data1 =~ /\r./; $data2 = "\n" if $data2 =~ /\r./; $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir}; $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir}; $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir}; $length0 = length($data0); $length1 = length($data1); $length2 = length($data2); $length = $length0; $data = $data0; # # Process Data # if ($dir0 eq "A") { if ($state eq "output") { if ($length0 > $minsize) { $length -= $minsize; $outsize += $length; } ### Data -> Keystrokes if ($dir1 eq "B" && $length1 == $minsize) { $delay .= "s $outsize\n"; $delay .= sprintf("t %.6f\n",$time0 - $outtime); $delay .= " \n"; $outsize = 0; $outtime = $time0; $state = "key"; } } } else { ### Keystroke if ($length0 == $minsize) { if ($data0 eq "\n") { $delay .= "k \\n\n"; } else { $delay .= "k $data0\n"; } } ### Keystroke -> Keystroke delay if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && $length2 == $minsize) { $timediff2 = $time2 - $time0; $delay .= sprintf("d %.6f\n",$timediff2); $outsize = 0; $outtime = $time0; } if ($length0 > $minsize) { $length -= $minsize; } ### Keystrokes -> Data if ( ($length0 == $minsize && (($length1 + $length2) > ($minsize * 2))) || ($dir1 eq "A" && $dir2 eq "A") ) { if ($length1 > $minsize) { $delay .= "r 1\n"; $delay .= sprintf("p %.6f\n",$timediff1); } else { $delay .= "r 2\n"; $delay .= sprintf("p %.6f\n",$timediff2); } $state = "output"; } } } # # Text Database of time delays between possible keystrokes # $filename3 = "session_${numtext}.${service_name}.keydata"; open (DELAY,">$filename3") || die "ERROR38: A pink jelly hits you. You die. $filename3 $!\n"; print DELAY "$delay \n"; close DELAY; # # Update Global Vars to remember new filenames # $Index{HTML}[$number] .= "
  • $filename3" . "
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s\n", '"' , " $filename3","",""); } # Save_Stream_Replay - Save a replay program for this stream. eg, dns. # sub Save_Stream_Replay { my ($filename,$duration); my $session_id = shift; my $number = shift; my $service_name = shift; my $numtext = sprintf("%04d",$number); ### Output $filename = "stream_${numtext}.${service_name}.replay"; $duration = ($UDP{id}{$session_id}{EndTime} - $UDP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); open (REPLAY,">$filename") || die "ERROR39: creating $filename $!\n"; binmode(REPLAY); # for backward OSs # # Create a perl program, that when run itself will print out # the contents of the server 1-way stream, with pauses based on # the packet arrival times (replay the stream in realtime). # print REPLAY "#!$PERL\n"; print REPLAY <<'END'; # # This is a UDP replay program. It will replay a stream using # the timestamps from the packet log. # # USAGE: run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./stream_0002.telnet.replay 2 # # Auto generated by Chaosreader. # $| = 1; $factor = $ARGV[0] || 1; sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } END # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = keys (%{$UDP{id}{$session_id}{time}}); @Times = sort { $a <=> $b } @Times; for ($i=0; $i <= $#Times; $i++) { # required ### Calculate time diff if possible if ($i == $#Times) { $timediff = 0; } else { $timediff = $Times[$i+1] - $Times[$i]; if ($timediff < 0) { $timediff = 0; } } $time = $Times[$i]; ### Fetch data from mem $data = $UDP{id}{$session_id}{time}{$time}; delete $UDP{id}{$session_id}{time}{$time}; # # Clean the data a little (order important) # $data =~ s/\\/\\\\/g; # backslash the backslashes $data =~ s/'/\\'/g; # backslash single quotes # # Now output the data in the replay program # print REPLAY "print '" . $data . "';\n"; # # This causes the replay program to pause # print REPLAY "ms($timediff);\n"; } close REPLAY; ### Better make it executable chmod (0755, "$filename"); ### Global Vars $Index{HTML}[$number] .= "
  • $filename" . " $duration seconds
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename","",$duration); } # Save_Session_XReplay - Save a replay program for this session. eg, X11. # This processes far more of the X11 protocol than I was hoping. # (xscope and ethereal were used to analyse X11). # sub Save_Session_XReplay { my $session_id = shift; my $number = shift; my $service_name = shift; my $numtext = sprintf("%04d",$number); my ($filename,$duration,$xcode,$xres_old,$xrest,$xwnum,$xdiff, $xlength,$xmsb,$xstart,$xjunk,$xvalue,$readnow,$data,$newdata, $n,$N,$chars,$y,$timediff,$texttimediff,$checkdepth,$filename2, $x11type); my @xWords; ### Initials $xmsb = ""; $readnow = 0; $xres_old = -1; $checkdepth = 0; # # Output - Main X11 replay program # $filename = "session_${numtext}.${service_name}.replay"; $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); open (REPLAY,">$filename") || die "ERROR40: creating $filename $!\n"; binmode(REPLAY); # for backward OSs # # Output - Text (keystroke replay) # $filename2 = "session_${numtext}.text${service_name}.replay"; open (REPLAY2,">$filename2") || die "ERROR41: creating $filename2 $!\n"; binmode(REPLAY2); # for backward OSs # --- textX11 --- # # Create a perl program, that when run itself will print out # the contents of the server 1-way stream, with pauses based on # the packet arrival times (replay the session in realtime). # print REPLAY2 "#!$PERL\n"; print REPLAY2 <<'END'; # # This is an X11 text replay program. It will replay keystrokes and text # of an X11 session using the timestamps from the packet log. # # USAGE: run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.textX11.replay 2 # # Auto generated by Chaosreader. # $| = 1; $factor = $ARGV[0] || 1; sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } END # --- X11 --- # # Create a perl program, that when run itself will print out # the contents of the server 1-way stream, with pauses based on # the packet arrival times (replay the session in realtime). # print REPLAY "#!$PERL\n"; print REPLAY <<'END'; # # This is a X11 replay program. It will replay a session using # the timestamps from the packet log, and transpose the X11 protocol so # that it can be redisplayed. You must have captured from the start # of the connection for this to work. # # USAGE: ./session_0001.X11.replay [-d destination host] [-p port] factor # # just run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.X11.replay 2 # a different host and port can be specified if needed. eg, # ./session_0002.X11.replay -d 192.168.1.5 -p 6001 # # PROBLEMS: you may need to authorise this connection to the X11 server # before it works. You could run "xhost +hostname" beforehand. # The playback needs to have captured the start of the connection. # Check you support the same colour depth as the playback. And check # the playback file simply isn't too big! (more than 500 Kb is # currently problematic). # # # Auto generated by Chaosreader. # use IO::Socket; use Getopt::Std; if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); } # Try fetching values from $DISPLAY ($hostdef,$portdef) = $ENV{DISPLAY} =~ /([^:]*):(\d*)/; $hostdef = "127.0.0.1" if $hostdef eq ""; $portdef += 6000; # Command line options take preference &getopts('d:p:'); if (defined $opt_d) { $host = $opt_d; } else { $host = $hostdef; } if (defined $opt_p) { $port = $opt_p; } else { $port = $portdef; } $factor = $ARGV[0] || 1; $DEBUG = 0; $| = 1; print "Chaosreader X11 Replay (experimental)\n\n"; print "Connecting to $host:$port\n"; print "(problems? try running \"xhost +hostname\" first).\n\n"; # --- Open Socket --- # $remote = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port, ); unless ($remote) { die "ERROR42: Can't connect to X11 daemon on $host:$port"; } $remote->autoflush(1); # --- Subroutines --- # # ms - sleeps for specified milliseconds # sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } # help - print help # sub help { open (MYSELF,"$0") || die "ERROR43: I can't see myself: $!\n"; @Myself = ; close MYSELF; ### Print comment from top of code foreach $line (@Myself) { last if $line !~ /^#/; next if $line =~ m:^#!/usr/bin/perl:; $line =~ s/^#/ /; print $line; } print "\n"; exit(0); } # R - recalculates and prints a resourse setting # The single character subroutine name saves on file space below. # sub R { #$offset = shift; #$new = $res + $offset; my $rid = shift; my $new; # final checks $diff = $rid - $ridbaseold; $diff = -$diff if $diff < 0; if ((($rid < $ridbaseold) && ($rid < 8196)) || ($diff > 8196)) { if ($msb) { return pack('N',$rid); } else { return pack('V',$rid); } } $new = $rid & $ridmaskold; $new = $new | $ridbase; if ($msb) { return pack('N',$new); } else { return pack('V',$new); } } # D - prints the new Drawable, usually the rootid. # sub D { my $rid = shift; # final checks if ($rid >= $ridbaseold) { # return mapped resource id return R($rid); } # return rootid if ($msb) { return pack('N',$rootid); } else { return pack('V',$rootid); } } # C - prints the new Colour map. # sub C { my $rid = shift; # final checks if ($rid >= $ridbaseold) { # return mapped resource id return R($rid); } # return colour map if ($msb) { return pack('N',$colour); } else { return pack('V',$colour); } } # M - Returns a generic mapped id. Can be rootid, colour, or resource. # These are used in Xcodes involving a mask. # sub M { my $rid = shift; # final checks if ($rid >= $ridbaseold) { # return mapped resource id return R($rid); } # return rootid map if ($rid == $rootidold) { if ($msb) { return pack('N',$rootid); } else { return pack('V',$rootid); } } # return colour map if ($rid == $colourold) { if ($msb) { return pack('N',$colour); } else { return pack('V',$colour); } } # return other if ($msb) { return pack('N',$rid); } else { return pack('V',$rid); } } # P - Check depth pixels, print warning if there is a mismatch. # sub P { my $depth = shift; if (! defined $Depth{$depth}) { print "\nWARNING: requested depth $depth may not be ". "supported by the server?\n"; } } # debug - print out a value # sub debug { my $word = shift; my $num = shift; my $pack = pack("N",$num); print "$word: $num ", sprintf("%2.2x%2.2x%2.2x%2.2x\n",unpack("C*",$pack)); } # --- MAIN --- # print "Sending X11 traffic:"; END ### Fetch raw data $xserver = &TCP_Follow_RawA($session_id); # # Determine endian of this transfer. Reading the # second short on MSB gives 11, and on LSB 2816 # (at least in testing). We split the difference # on 256 (is case there is a little variation). # ($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver); # # Create aliases for "n" and "N" so I can think # in big endian. # if ($xvalue < 256) { $xmsb = 1; $n = "n"; $N = "N"; } else { $xmsb = 0; $n = "v"; $N = "V"; } my ($success,$major,$minor,$length,$release,$ridbase, $ridmask,$mbsize,$vendor,$reqmax,$roots,$formats,$ibo, $bbo,$bslu,$bslp,$keymin,$keymax,$pad,$rest) = unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$xserver); ($x11type,$rest) = unpack("a${vendor}a*",$rest); $pad = ((4 - ($vendor % 4)) % 4); ($junk,$rest) = unpack("a${pad}a*",$rest); foreach $i (1..$formats) { ($junk,$rest) = unpack("a8a*",$rest); } ($rootid,$colour,$junk) = unpack("$N${N}a*",$rest); # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { push(@Times,$time) } } @Times = sort { $a <=> $b } @Times; # # --- Main Loop --- # # (this needs to be a for loop!) for ($i=0; $i <= $#Times; $i++) { ### Calculate time diff if possible if ($i == $#Times) { $timediff = 0; } else { $timediff = $Times[$i+1] - $Times[$i]; # just in case, if ($timediff < 0) { $timediff = 0; } } $time = $Times[$i]; $texttimediff += $timediff; ### Fetch data from mem $data = $TCP{id}{$session_id}{time}{$time}{data}; ### If initial request was fetched, if ($readnow == 0) { ### Populate $xstart with initial request $xstart .= $data; # # This triggers the replay program to ask the X11 # server for the connection data - which # needs to be processed so that various # resource offsets can be used later on. # if (length($xstart) >= 12) { $readnow = 1; } } else { # # Change resource offsets # (reads $data and writes to $data) # $xrest = $data; $data = ""; # output stream of data & subs # # Process through X11 codes # while (length($xrest) > 0) { ($xcode,$xbyte,$xlength,$xrest) = unpack("CC${n}a*",$xrest); ### Add xcode to output stream $data $d = pack("CC${n}",$xcode,$xbyte,$xlength); # the unusual seds $d =~ s/\\/\\\\/g; $d =~ s/'/\\'/g; $d =~ s/\015\012/'."\\015\\012".'/gs; $data .= $d; # # Fetch code values from $xrest, and trim # $xrest. For most requests, the value length # is a field (bytes 3,4) except for XErrors # (code 0) where the total length is always 32. # if ($xcode == 0) { $xlv = 28; } else { $xlv = ($xlength - 1) * 4; $xlv = -$xlv if $xlv < 0; } while (length($xrest) < $xlv) { # some more magic $i++; last if ($i > $#Times); $next = $Times[$i]; ### Fetch data from mem $xrest .= $TCP{id}{$session_id}{time}{$next}{data}; } ($xvalue,$xrest) = unpack("a${xlv}a*",$xrest); #$format = "%2.2x%2.2x " x ($xlv/2); #printf("X$xcode: $xbyte,$xlength $format\n", # unpack("C*",$xvalue)); ### Debug $xwnum = 0; @xWords = unpack("${N}*",$xvalue); # # If this is a text event, save the text to the # textX11 replay program. # if (($xcode == 76) || ($xcode == 77)) { # Check if this is a xImageText16Req if ($xcode == 77) { $xbyte *= 2; } ($pad,$y,$chars) = unpack("a10${n}a$xbyte",$xvalue); if ($yold != $y) { $chars = "\n$chars"; } ### Clean the data a little (order important) $chars =~ s/\\/\\\\/g; $chars =~ s/'/\\'/g; $chars =~ s/\0//g; ### Now output the data in the replay program print REPLAY2 "print '" . $chars . "';\n"; ### This causes the replay program to pause print REPLAY2 "ms($texttimediff);\n" unless $texttimediff < 0.002; $yold = $y; $texttimediff = 0; } # # Process a text scroll event (by using 62 - copy area) # if ($xcode == 62) { print REPLAY2 "print \"\\n\";\n"; $chars = "\n"; } # # If this is a create window event, check the depth. # if (($xcode == 1) && ($checkdepth == 0)) { $data .= "',P($xbyte),'"; $checkdepth = 1; } # # Print the X11 data with embedded subroutines # to transpose the resource IDs. # foreach $xw (@xWords) { $xwnum++; if ($X11_Codes[$xcode][$xwnum] == 1) { $data .= "',R($xw),'"; #print "XCODER: $xcode, $xwnum\n"; } elsif ($X11_Codes[$xcode][$xwnum] == 2) { $data .= "',D($xw),'"; #print "XCODED: $xcode, $xwnum\n"; } elsif ($X11_Codes[$xcode][$xwnum] == 3) { $data .= "',C($xw),'"; #print "XCODEC: $xcode, $xwnum\n"; } elsif ($X11_Codes[$xcode][$xwnum] == 4) { $data .= "',M($xw),'"; #print "XCODEM: $xcode, $xwnum\n"; } else { $d = pack("$N",$xw); $d =~ s/\\/\\\\/g; $d =~ s/'/\\'/g; $d =~ s/\015\012/'."\\015\\012".'/gs; $data .= $d; } } } } # # Now output the data in the replay program # print REPLAY "print '.';\n"; print REPLAY "print \$remote '" . $data . "';\n"; if ($readnow == 1) { $readnow = 2; print REPLAY "\$msb = $xmsb;\n"; print REPLAY "\$ridbaseold = $ridbase;\n"; print REPLAY "\$ridmaskold = $ridmask;\n"; print REPLAY "\$rootidold = $rootid;\n"; print REPLAY "\$colourold = $colour;\n"; # # The following code implements the client to # server connection - we need to read the # resource and window IDs which are necessary # when transposing the replay traffic to # these new values. # print REPLAY <<'END'; if ($msb) { $n = "n"; $N = "N"; } else { $n = "v"; $N = "V"; } read($remote,$in,40); # (xConnSetup) ($success,$major,$minor,$length,$release,$ridbase,$ridmask,$mbsize,$vendor, $reqmax,$roots,$formats,$ibo,$bbo,$bslu,$bslp,$keymin,$keymax,$pad) = unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$in); read($remote,$in,$vendor); print "\nX11 Server Type: $in\n"; read($remote,$in,((4 - ($vendor % 4)) % 4)); foreach $i (1..$formats) { read($remote,$in,8); # (xPixmapFormat) ($depth,$junk) = unpack("Ca*",$in); $Depth{$depth} = 1; next if $depth == 1; print "X11 server supports $depth bit resolution\n"; } read($remote,$in,8); # (xWindowRoot) ($rootid,$colour,$junk) = unpack("$N$N",$in) unless defined $rootid; if ($DEBUG) { debug("Resource ID new: ",$ridbase); debug("Resource ID old: ",$ridbaseold); debug("Root ID new: ",$rootid); debug("Root ID old: ",$rootidold); debug("Colour map new: ",$colour); debug("Colour map old: ",$colourold); } END } # # This causes the replay program to pause # print REPLAY "ms($timediff);\n" unless $timediff < 0.002; # (efficiency). } print REPLAY "print \"\n\";\n"; print REPLAY "close \$remote;\n"; close REPLAY; ### Better make it executable chmod (0755, "$filename"); close REPLAY2; ### Better make it executable chmod (0755, "$filename2"); ### Global Vars $Index{HTML}[$number] .= "
  • $filename" . " $duration seconds
  • \n"; $Index{HTML}[$number] .= "
  • $filename2" . " $duration seconds
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename","",$duration); $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename2","",$duration); } # Save_Session_VNCReplay_andHTML - Save a replay program for this session. # This creates a program that is used in conjunction with vncviewer. # It also saves the HTML version (it would have been redundant to # create a seperate subroutine for that). # sub Save_Session_VNCReplay_andHTML { my $session_id = shift; my $number = shift; my $service_name = shift; my $session_text = shift; my $numtext = sprintf("%04d",$number); my ($filename,$filename2,$filename3,$duration,$code,$rest,$extra, $length,$start,$junk,$down,$value,$data,$oldtimediff,$printed,$chars, $char,$timediff,$checkdepth,$html); my @xWords; $oldtimediff = 0; $printed = 0; $html = ""; # # Output - Text (keystroke replay) # $filename2 = "session_${numtext}.text${service_name}.replay"; open (REPLAY2,">$filename2") || die "ERROR44: creating $filename2 $!\n"; binmode(REPLAY2); # for backward OSs # # --- textVNC --- # # Create a perl program, that when run itself will print out # the contents of the client 1-way stream, with pauses based on # the packet arrival times (replay the session in realtime). # print REPLAY2 "#!$PERL\n"; print REPLAY2 <<'END'; # # This is an VNC text replay program. It will replay keystrokes from # a VNC session using the timestamps from the packet log. # # USAGE: run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.textVNC.replay 2 # # Auto generated by Chaosreader. # $| = 1; $factor = $ARGV[0] || 1; sub ms { $ms = shift; $ms = $ms / $factor; select(undef, undef, undef, $ms); } END # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { push(@Times,$time) } } @Times = sort { $a <=> $b } @Times; # # --- Main Loop --- # # (this needs to be a for loop!) for ($i=0; $i <= $#Times; $i++) { ### Calculate time diff if possible if ($i == $#Times) { $timediff = 0; } else { $timediff = $Times[$i+1] - $Times[$i]; # just in case, if ($timediff < 0) { $timediff = 0; } } $time = $Times[$i]; ### Fetch data from mem $data = $TCP{id}{$session_id}{time}{$time}{data}; ($code) = unpack("C",$data); $chars = ""; # skip code 0's if ($code > 0) { # # Process through VNC client codes # $chars = ""; while (length($data) > 0) { ($code) = unpack("C",$data); $length = $VNC_Code_Size{$code}; $length--; last if $length <= 0; # Fetch this code only ($code,$value,$data) = unpack("Ca${length}a*",$data); ### Process Key Pressed if ($code == 4) { ($down,$junk,$extra,$char) = unpack("Ca4Ca",$value); next if $down == 0; # record key-ups if ($extra == 0) { $chars .= $char; } else { if (defined $KeyCode{vnc}{0}{$char}) { $chars .= $KeyCode{vnc}{0}{$char}; } } $html .= $chars; } } } $chars =~ s/\\/\\\\/g; $chars =~ s/'/\\'/g; ### Now output the data in the replay program unless (length($chars) == 0) { print REPLAY2 "ms($oldtimediff);\n" unless $oldtimediff < 0.002; ### Print the data print REPLAY2 "print '" . $chars . "';\n"; # these counters are for efficiency, otherwise # we print too many sequiential sleeps $printed = 1; $oldtimediff = 0; } else { $printed = 0; $oldtimediff += $timediff; next; } ### This causes the replay program to pause print REPLAY2 "ms($timediff);\n" unless $timediff < 0.002; } close REPLAY2; ### Better make it executable chmod (0755, "$filename2"); # --- HTML --- # # Create a HTML page showing the keystrokes ### Clean up html $html = &Desex_HTML($html); ### Output $filename3 = "session_${numtext}.text${service_name}${ext}.html"; open (OUT,">$filename3") ||die "ERROR45: file create, $filename3: $!\n"; binmode(OUT); print OUT "\n\n" . "

    $service_name: $session_text

    \n" . "

    File $Arg{infile}, Session $number

    \n" . "
    \n" .
             "" .$html. "
    \n\n\n"; close OUT; ### Global Vars $length = length($html); $Index{HTML}[$number] .= "
  • keystrokes
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename3","",$length); # # Output - Main VNC replay program # $filename = "session_${numtext}.${service_name}.replay"; $duration = ($TCP{id}{$session_id}{EndTime} - $TCP{id}{$session_id}{StartTime}); $duration = sprintf("%.0f",$duration); open (REPLAY,">$filename") || die "ERROR46: creating $filename $!\n"; binmode(REPLAY); # for backward OSs # # --- VNC --- # # Create a perl program, that when run itself will create a # playback VNC server that listens on a port. When a vncviewer # connects, the contents of the server 1-way stream arew played back, # with pauses. # print REPLAY "#!$PERL\n"; print REPLAY <<'END'; # # This is a VNC replay program. This runs as a server and listens on a port, # then vncviewer is run to connect to that port - at which point the playback # commences. # # USAGE: ./session_0001.VNC.replay [-p port] factor # # just run the script as normal. You can provide a factor as an # argument, eg "2" to run twice as fast, or "0.5" to run # at half time. eg, # ./session_0002.VNC.replay 2 # a different host and port can be specified if needed. eg, # ./session_0002.VNC.replay -p 5925 # # After the script is running, connect using vncviewer. eg, # vncviewer -viewonly localhost:25 # # PROBLEMS: The playback needs to have captured the start of the connection, # you need to be at the same colour depth as the playback (or more may # work), and your screen should be at least as big as the playback # resolution. Newer versions of vncviewer may be tuned to match the # playback (eg "-8bit"). # # Auto generated by Chaosreader. # use IO::Socket; use Getopt::Std; use Net::hostent; $| = 1; if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); } # Command line options take preference &getopts('p:'); if (defined $opt_p) { $port = $opt_p; } else { $port = 5921; } $vncport = $port - 5900; if ($vncport < 0) { die "ERROR47: Port $port too low, use at least 5901.\n"; } $factor = $ARGV[0] || 1; $DEBUG = 0; print "Chaosreader VNC Replay (experimental)\n\n"; print "Listening on port $port...\n"; # --- Open Socket --- # $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $port, Listen => SOMAXCONN, Reuse => 1); die "can't setup server" unless $server; unless ($server) { die "ERROR48: Can't open port $port. Try a different port."; } print <; close MYSELF; ### Print comment from top of code foreach $line (@Myself) { last if $line !~ /^#/; next if $line =~ m:^#!/usr/bin/perl:; $line =~ s/^#/ /; print $line; } print "\n"; exit(0); } # # --- MAIN --- # ### Wait for connection $client = $server->accept(); $client->autoflush(1); print "Sending VNC traffic:"; END # # Sort the data on the timestamps, calculating timestamp differences # to record in the replay program. # @Times = (); foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { push(@Times,$time) } } @Times = sort { $a <=> $b } @Times; # # --- Main Loop --- # # (this needs to be a for loop!) for ($i=0; $i <= $#Times; $i++) { ### Calculate time diff if possible if ($i == $#Times) { $timediff = 0; } else { $timediff = $Times[$i+1] - $Times[$i]; # just in case, if ($timediff < 0) { $timediff = 0; } } $time = $Times[$i]; ### Fetch data from mem $data = $TCP{id}{$session_id}{time}{$time}{data}; $data =~ s/\\/\\\\/g; $data =~ s/'/\\'/g; $data =~ s/\015\012/'."\\015\\012".'/gs; # # Now output the data in the replay program # print REPLAY "print '.';\n"; print REPLAY "print \$client '" . $data . "';\n"; # # This causes the replay program to pause # print REPLAY "ms($timediff);\n" unless $timediff < 0.002; # (efficiency). } print REPLAY "print \"\n\";\n"; print REPLAY "close \$client;\n"; close REPLAY; ### Better make it executable chmod (0755, "$filename"); ### Global Vars $Index{HTML}[$number] .= "
  • $filename" . " $duration seconds
  • \n"; $Index{HTML}[$number] .= "
  • $filename2" . " $duration seconds
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename","",$duration); $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", '"' , " $filename2","",$duration); } # Save_SMTP_Emails - Save emails from an SMTP session. # sub Save_SMTP_Emails { my ($filename); my $session_id = shift; my $number = shift; my $service_name = "smtp"; my $numtext = sprintf("%04d",$number); ### Full - Input $snmp_data = &TCP_Follow_RawB($session_id); ### Full - Processing @Snmp_parts = split(/\r\n\.\r\n|\n\.\n/,$snmp_data); ### LOOP $partnum = 0; foreach $snmp_part (@Snmp_parts) { next unless $snmp_part =~ /DATA/; $partnum++; $parttext = sprintf("%02d",$partnum); ### Part - Processing $snmp_part =~ s/^.*DATA\r?\n//s; # '/s;' is new perl5, # else '/;' with $* = 1 ### Part - Output if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } else { $ext = ""; } $filename = "session_${numtext}.part_${parttext}." . "${service_name}${ext}.email"; open (OUT,">$filename") || die "ERROR50: file create, $filename: $!\n"; binmode(OUT); # for backward OSs print OUT $snmp_part; close OUT; ### Part - Global Vars my $length = length($snmp_part); $Index{HTML}[$number] .= "
  • $filename" . " $length bytes
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); } } # Save_HTTP_Files - Save HTTP components. # sub Save_HTTP_Files { my ($filename); my $session_id = shift; my $number = shift; my $service_name = shift; my $numtext = sprintf("%04d",$number); ### Full - Input $http_session = &TCP_Follow_RawA($session_id); ### Full - Processing @HttpParts = split(/HTTP\/[0-9.]* /,$http_session); ### LOOP $partnum = 0; foreach $http_part (@HttpParts) { ### Part - Processing ($http_header,$http_data) = split(/\r\n\r\n|\n\n/,$http_part,2); next if $http_data eq ""; next if length($http_data) < 8; $partnum++; $parttext = sprintf("%02d",$partnum); ### Part - Checks $http_type = &File_Type($http_data); if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } else { $ext = ""; } ### Part - Output $filename = "session_${numtext}.part_$parttext${ext}." . "$http_type"; open (OUT,">$filename") || die "ERROR51: file create, $filename: $!\n"; binmode(OUT); # for backward OSs print OUT $http_data; close OUT; ### Part - Global Vars my $length = length($http_data); $Index{HTML}[$number] .= "
  • $filename" . " $length bytes
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); if (&Is_Image($http_type)) { $Image{HTML}[$number]{links} .= " "; $Image{notempty} = 1; } } } # Save_NFS_File - Save NFS file. Only works well for some files, if the NFS # header can't be processed, a "*.nfs.raw" file is created. # sub Save_NFS_File { my ($filename); my $session_id = shift; my $number = shift; my $service_name = "nfs"; my $numtext = sprintf("%04d",$number); ### Input my $nfs_raw = &TCP_Follow_RawB($session_id); ### Processing ($nfs_start,$nfs_size,$nfs_end) = unpack('a56a4a*',$nfs_raw); $nfs_sizeint = unpack("N",$nfs_size); ($nfs_start,$nfs_data) = split(/$nfs_size....$nfs_size/,$nfs_end,2); ### Checks if (($nfs_sizeint > 4) && (length($nfs_data) >= $nfs_sizeint)) { $nfs_type = &File_Type($nfs_data); if ($nfs_sizeint < length($nfs_data)) { $nfs_data = unpack("a${nfs_sizeint}a*",$nfs_data); } } else { $nfs_type = "raw"; $nfs_data = $nfs_raw; } if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } else { $ext = ""; } ### Output $filename = "session_${numtext}.part_01.${service_name}${ext}.nfs." . "$nfs_type"; open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n"; binmode(OUT); # for backward OSs print OUT $nfs_data; close OUT; ### Global Vars my $length = length($nfs_data); $Index{HTML}[$number] .= "
  • $filename" . " $length bytes
  • \n"; $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", '"' , " $filename","",$length); } # TCP_Follow_RawA - process session by TCP Seq numbers 1-way. # (TCP ASSEMBLY) # sub TCP_Follow_RawA { my $session_id = shift; my $raw = ""; # # Assemble TCP Sessions. Each hash contains session_ids as keys, # and the value points to another hash of sequence numbers and data. # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output. # @Seqs = keys (%{$TCP{id}{$session_id}{Aseq}}); foreach $seq (sort { $a <=> $b } @Seqs) { $raw .= ${$TCP{id}{$session_id}{Aseq}{$seq}}; } return $raw; } # TCP_Follow_RawB - process session by TCP Seq numbers 1-way. # (TCP ASSEMBLY) # sub TCP_Follow_RawB { my $session_id = shift; my $raw = ""; # # Assemble TCP Sessions. Each hash contains session_ids as keys, # and the value points to another hash of sequence numbers and data. # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output. # @Seqs = keys (%{$TCP{id}{$session_id}{Bseq}}); foreach $seq (sort { $a <=> $b } @Seqs) { $raw .= ${$TCP{id}{$session_id}{Bseq}{$seq}}; } return $raw; } # Pick_Service_Port - pick which port is the server. Usually is the lower # number, however check if the direction is already known (eg SYN). # The port arguments will not often be needed. # # NOTE: This code is different to Generate_TCP_IPs - which does the "<->"'s # sub Pick_Service_Port { my $type = shift; my $id = shift; my $porta = shift; my $portb = shift; my $from_server = 0; my ($hi,$low); # Catch active FTP, etc. ($low,$hi) = sort { $a <=> $b } ($porta,$portb); if ($low < 100) { return ($low,$hi); } if ($type eq "TCP") { if (defined $TCP{id}{$id}{source}) { if ($TCP{id}{$id}{source} eq $TCP{id}{$id}{src}) { return ($TCP{id}{$id}{dest_port},$TCP{id}{$id}{src_port}); } else { return ($TCP{id}{$id}{src_port},$TCP{id}{$id}{dest_port}); } } } elsif ($type eq "UDP") { return ($UDP{id}{$id}{dest_port},$UDP{id}{$id}{src_port}); } # resort to a sort return sort { $a <=> $b } ($porta,$portb); } # Generate_SessionID - input source and dest IPs and ports, and generate # a unique session_id based on them. this is done by sorting on # ports and then IPs. Also returns a flag if the packet may be # assumed to be from_server - where the lowest port is assumed to # be the server (unless TCP SYNs have been observed). # sub Generate_SessionID { my $ip_src = shift; my $tcp_src_port = shift; my $ip_dest = shift; my $tcp_dest_port = shift; my $type = shift; my $from_server = 0; my $session_id; # # Generate session_id string using host:port,host:port sorted on # port (low port last). # if ($tcp_src_port < $tcp_dest_port) { $session_id = "$ip_dest:$tcp_dest_port,$ip_src:$tcp_src_port"; $from_server = 1; } elsif ($tcp_src_port > $tcp_dest_port) { $session_id = "$ip_src:$tcp_src_port,$ip_dest:$tcp_dest_port"; $from_server = 0; } else { $session_id =join(",",sort("$ip_src:$tcp_src_port", "$ip_dest:$tcp_dest_port")); $from_server = 1; } if ($type eq "TCP") { if (defined $TCP{id}{$session_id}{source}) { if ($TCP{id}{$session_id}{source} eq $ip_dest) { $from_server = 1; } else { $from_server = 0; } } } return ($session_id,$from_server); } # Generate_TCP_IDs - generate a text and html version of the session ID, that # displays direction of the TCP session if SYNs and ACKs were # observed, else uses a "<->" symbol to represent unknown # direction. TCP only. # sub Generate_TCP_IDs { my $session_id = shift; my ($ip_src,$tcp_src_port,$ip_dest,$tcp_dest_port,$text,$html); # try this direction, $ip_src = $TCP{id}{$session_id}{src}; $ip_dest = $TCP{id}{$session_id}{dest}; $tcp_src_port = $TCP{id}{$session_id}{src_port}; $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; if (defined $TCP{id}{$session_id}{source}) { if ($TCP{id}{$session_id}{source} eq $ip_dest) { # nope, switch ends $ip_src = $TCP{id}{$session_id}{dest}; $ip_dest = $TCP{id}{$session_id}{src}; $tcp_src_port = $TCP{id}{$session_id}{dest_port}; $tcp_dest_port = $TCP{id}{$session_id}{src_port}; } $text = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port"; $html = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port"; } else { $text = "$ip_src:$tcp_src_port <-> $ip_dest:$tcp_dest_port"; $html = "$ip_src:$tcp_src_port <-> " . "$ip_dest:$tcp_dest_port"; } return ($text,$html); } # Generate_IP_ID - input source IP, dest IP and ident, and generate a # unique ip_id based on them. This is necessary for IP # fragmentation reassembely. Normally we would assume that # the IP_ident was unique - however this program could # process traffic from many different hosts over a long # period of time - idents alone could clash. # sub Generate_IP_ID { my $ip_src = shift; my $ip_dest = shift; my $ip_ident = shift; my $ip_id; # # Generate ip_id string using host:host:ident sorted on IP. # # $ip_id = join(",",sort("$ip_src","$ip_dest")) . ",$ip_ident"; return $ip_id; } # Read_Tcpdump_Record - Read the next tcpdump record, will "last" if # there are no more records. # sub Read_Tcpdump_Record { my $more; ### Fetch record header $length = read(INFILE,$header_rec,($integerSize * 2 + 8)); ### Quit main loop if at end of file last if $length < 16; ### Throw out extra info in tcpdump/modified1 format if ($STYLE =~ /^modified/) { $length = read(INFILE,$more,8); } $frame++; ## Unpack header, endian sensitive if ($STYLE =~ /1$/) { ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length, $tcpdump_length_orig) = unpack('NNNN',$header_rec); } else { ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length, $tcpdump_length_orig) = unpack('VVVV',$header_rec); } $length = read(INFILE,$tcpdump_data,$tcpdump_length); $tcpdump_drops = $tcpdump_length_orig - $tcpdump_length; } # Read_Snoop_Record - Read the next snoop record, will "last" if # there are no more records. # sub Read_Snoop_Record { ### Fetch record header $length = read(INFILE,$header_rec,24); ### Quit main loop if at end of file last if $length < 24; $frame++; ### Unpack header ($snoop_length_orig,$snoop_length_inc,$snoop_length_rec,$snoop_drops, $snoop_seconds,$snoop_msecs) = unpack('NNNNNN',$header_rec); $length = read(INFILE,$snoop_data,$snoop_length_inc); $skip = read(INFILE,$pad,($snoop_length_rec - $snoop_length_inc - 24)); } # Load_Index_File - Load the master index file "index.file" into @Master # sub Load_Index_File { my ($path,$dir,$file,$start,$end,$duration,$index); # # Load index.file lines into memory # open (FILES,"index.file") || die "ERROR53: Can't read index.file: $!\n" ."Standalone mode needs to have run recently from this directory.\n\n"; chomp(@Files = ); close FILES; # # Populate @Master # $index = 0; foreach $path (@Files) { ($dir,$file,$duration,$start,$end) = split(/\t/,$path); $Master[$index]{starttime} = $start; $Master[$index]{endtime} = $end; $Master[$index]{dir} = $dir; $Master[$index]{file} = $file; $Master[$index]{duration} = $duration; $Master[$index]{size} = -s "$dir/$file"; $index++; } } # Load_Etc_Services - load /etc/services lookup table into memory, # into %Services_TCP and %Services_UDP. # sub Load_Etc_Services { my ($line,$name,$service); ### Hardcoded %Services_TCP = (20 => "ftp-data", 21 => "ftp", 23 => "telnet", 25 => "smtp", 80 => "web", 109 => "pop2", 110 => "pop3", 143 => "imap", 513 => "login", 514 => "shell", 3128 => "web", 4110 => "irc4110", 5000 => "irc5000", 6000 => "X11", 6660 => "irc", 6665 => "irc", 6666 => "irc", 6667 => "irc", 6668 => "irc", 6669 => "irc", 7000 => "irc7000", 8000 => "irc8000", 8080 => "web", 9000 => "irc9000"); # non standard IRC ports include the number in their name foreach (@Save_As_X11_Playback_Ports) { $Services_TCP{$_} = "X11"; } foreach (@Save_As_VNC_Playback_Ports) { $Services_TCP{$_} = "VNC"; } %Services_UDP = (53 => "dns"); ### File input open(SERVICES,"/etc/services") || return; while ($line = ) { next if $line =~ /^#|^\s*$/; # skip comments, blank lines. if ($line =~ /\d\/tcp/) { $is_tcp = 1; } else { $is_tcp = 0; } $line =~ s:/.*::; ($name,$port) = split(' ',$line); if ($is_tcp) { $Services_TCP{$port} = $name; } else { $Services_UDP{$port} = $name; } } close SERVICES; } # Set_IP_Protocols - Set a lookup hash for IP Protocols to names. # RFC790, RFC1700. # sub Set_IP_Protocols { %IP_Protocols = (0 => "Reserved", 1 => "ICMP", 2 => "Unassigned", 3 => "Gateway-to-Gateway", 4 => "CCMC Gateway Monitoring Message", 5 => "ST", 6 => "TCP", 7 => "UCL", 8 => "Unassigned", 9 => "Secure", 10 => "BBN RCC Monitoring", 11 => "NVP", 12 => "PUP", 13 => "Pluribus", 14 => "Telenet", 15 => "XNET", 16 => "Chaos", 17 => "UDP", 18 => "Multiplexing", 19 => "DCN", 20 => "TAC Monitoring", 37 => "DDP", 41 => "SIP", 42 => "SDRP", 44 => "IPv6 Frag", 50 => "SIPP-ESP", 51 => "SIPP-AH", 53 => "SWIPE", 50 => "SDRP", 58 => "ICMPv6", 88 => "IGRP", 94 => "IPIP" ); } # Set_ICMP_Types - Set a lookup hash for ICMP Types. RFC792. # sub Set_ICMP_Types { %ICMP_Types = (0 => "Echo Reply", 3 => "Destination Unreachable", 4 => "Source Quench", 5 => "Redirect", 8 => "Echo", 11 => "Time Exceeded", 12 => "Parameter Problem", 13 => "Timestamp", 14 => "Timestamp Reply", 15 => "Information Request", 16 => "Information Reply", 128 => "Echo", 129 => "Echo Reply", 135 => "Neighbor solicitation", 136 => "Neighbor advertisement" ); } # Set_Result_Names - Set a lookup hash for squid result codes. # (This needs some fine tuning). # sub Set_Result_Names { %Result_Names = ("" => "TCP_MISS", 000 => "TCP_MISS", 200 => "TCP_HIT", 302 => "TCP_HIT", 304 => "TCP_REFRESH_HIT", 404 => "TCP_NEGATIVE_HIT" ); } # Set_X11_Codes - creates a lookup hash needed for X11 transposing. # sub Set_X11_Codes { # # This has a row per X11 code, the row describing the 16 bit # words that make up the values. "1" means resource id. # (some values are 8 bit, but are fortunately padded). # @X11_Codes = ( [ 0 ], # X_Error entry [ 0, 2, 2, 0, 0, 0, 1, 0,4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateWindow 1 [ 0, 1, 0 ], # X_ChangeWindowAttributes [ 0, 1 ], # X_GetWindowAttributes [ 0 ], # X_DestroyWindow? [ 0 ], # X_DestroySubwindows? [ 0, 1 ], # X_ChangeSaveSet [ 0, 1, 1, 0 ], # X_ReparentWindow [ 0, 1 ], # X_MapWindow [ 0, 1 ], # X_MapSubwindows [ 0, 1 ], # X_UnmapWindow 10 [ 0, 1 ], # X_UnmapSubwindows [ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ConfigureWindow [ 0, 1 ], # X_CirculateWindow [ 0, 2 ], # X_GetGeometry [ 0, 1 ], # X_QueryTree [ 0, 1 ], # X_InternAtom (? else 0,0) [ 0 ], # X_GetAtomName? [ 0, 1, 0, 0, 1, 0 ], # X_ChangeProperty (? else 0,1,0,0,0,0) [ 0, 1, 0 ], # X_DeleteProperty [ 0, 2, 0, 0, 0, 0 ], # X_GetProperty 20 [ 0 ], # X_ListProperties? [ 0, 1, 0, 0 ], # X_SetSelectionOwner [ 0 ], # X_GetSelectionOwner [ 0, 1, 0, 0, 0, 0 ], # X_ConvertSelection [ 0, 1, 0 ], # X_SendEvent [ 0, 1, 0, 1, 0, 0 ], # X_GrabPointer [ 0, 1, 0 ], # X_UngrabPointer? [ 0, 1, 0, 1, 0, 0 ], # X_GrabButton [ 0, 1, 0 ], # X_UngrabButton [ 0, 1, 0, 0 ], # X_ChangeActivePointerGrab 30 [ 0, 1, 0, 0 ], # X_GrabKeyboard [ 0, 1, 0 ], # X_UngrabKeyboard? [ 0, 1, 0, 0 ], # X_GrabKey [ 0, 1, 0 ], # X_UngrabKey [ 0, 0, 0 ], # X_AllowEvents [ 0 ], # X_GrabServer? [ 0 ], # X_UngrabServer? [ 0 ], # X_QueryPointer? [ 0, 1, 0, 0 ], # X_GetMotionEvents [ 0, 1, 1, 0 ], # X_TranslateCoords 40 [ 0, 1, 1, 0, 0, 0 ], # X_WarpPointer [ 0, 1, 0 ], # X_SetInputFocus [ 0 ], # X_GetInputFocus? [ 0 ], # X_QueryKeymap? [ 0, 1, 0 ], # X_OpenFont [ 0, 1 ], # X_CloseFont [ 0, 1 ], # X_QueryFont [ 0, 1 ], # X_QueryTextExtents [ 0, 0 ], # X_ListFonts [ 0, 0 ], # X_ListFontsWithInfo 50 [ 0, 0 ], # X_SetFontPath [ 0 ], # X_GetFontPath? [ 0, 1, 2, 0 ], # X_CreatePixmap [ 0 ], # X_FreePixmap? [ 0, 1, 2, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateGC ?(else 0,1,1,0) [ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeGC [ 0, 1, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CopyGC [ 0, 1, 0 ], # X_SetDashes [ 0, 1, 0 ], # X_SetClipRectangles [ 0, 1 ], # X_FreeGC? 60 [ 0, 1, 0, 0 ], # X_ClearArea [ 0, 2, 2, 1, 0, 0, 0 ], # X_CopyArea [ 0, 2, 2, 1, 0, 0, 0, 0 ], # X_CopyPlane [ 0, 2, 1 ], # X_PolyPoint [ 0, 2, 1 ], # X_PolyLine [ 0, 2, 1 ], # X_PolySegment [ 0, 2, 1 ], # X_PolyRectangle [ 0, 2, 1 ], # X_PolyArc [ 0, 2, 1, 0 ], # X_FillPoly [ 0, 2, 1 ], # X_PolyFillRectangle 70 [ 0, 2, 1 ], # X_PolyFillArc [ 0, 2, 1, 0, 0, 0 ], # X_PutImage [ 0, 2, 0, 0, 0 ], # X_GetImage [ 0, 2, 1, 0 ], # X_PolyText8 [ 0, 2, 1, 0 ], # X_PolyText16 [ 0, 2, 1, 0 ], # X_ImageText8 [ 0, 2, 1, 0 ], # X_ImageText16 [ 0, 3, 1, 1 ], # X_CreateColormap [ 0 ], # X_FreeColormap? [ 0, 3, 3 ], # X_CopyColormapAndFree 80 [ 0 ], # X_InstallColormap? [ 0 ], # X_UninstallColormap? [ 0 ], # X_ListInstalledColormaps? [ 0, 3, 0, 0 ], # X_AllocColor [ 0, 3, 0 ], # X_AllocNamedColor [ 0, 3, 0 ], # X_AllocColorCells [ 0, 3, 0, 0 ], # X_AllocColorPlanes [ 0, 3, 0 ], # X_FreeColors [ 0, 3 ], # X_StoreColors [ 0, 3, 0, 0 ], # X_StoreNamedColor 90 [ 0, 3 ], # X_QueryColors [ 0, 3, 0 ], # X_LookupColor [ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateCursor [ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateGlyphCursor [ 0 ], # X_FreeCursor? [ 0, 1, 0, 0, 0 ], # X_RecolorCursor [ 0, 2, 0 ], # X_QueryBestSize [ 0, 1 ], # X_QueryExtension (? else 0,0) [ 0, 0, 0 ], # X_ListExtensions? [ 0, 1, 0 ], # X_ChangeKeyboardMapping 100 [ 0, 1, 0 ], # X_GetKeyboardMapping [ 0, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeKeyboardControl [ 0, 0, 0 ], # X_GetKeyboardControl? [ 0 ], # X_Bell [ 0, 0, 0 ], # X_ChangePointerControl [ 0, 0, 0 ], # X_GetPointerControl? [ 0, 0, 0 ], # X_SetScreenSaver [ 0, 0, 0 ], # X_GetScreenSaver? [ 0, 0 ], # X_ChangeHosts [ 0 ], # X_ListHosts 110 [ 0 ], # X_SetAccessControl [ 0 ], # X_SetCloseDownMode [ 0, 0, 0 ], # X_KillClient? [ 0, 1, 0 ], # X_RotateProperties [ 0 ], # X_ForceScreenSaver [ 0 ], # X_SetPointerMapping [ 0, 0, 0 ], # X_GetPointerMapping? [ 0 ], # X_SetModifierMapping [ 0, 0, 0 ], # X_GetModifierMapping? [ 0 ], # undef 120 [ 0 ], # undef [ 0 ], # undef [ 0 ], # undef [ 0 ], # undef [ 0 ], # undef [ 0 ], # undef [ 0, 0, 0 ] # X_NoOperation 127 ); } # Set_X11_KeyCodes - creates a lookup hash of X11 Key codes needed # to generate coloured 2-way HTML X11 reports. # sub Set_X11_KeyCodes { my ($junk,$code,$char1,$char2,$line, $sun_xmodmap_pke,$linux_xmodmap_pke); my %Alias; # # These are generated using "xmodmap -pke" (and trimmed a little). # $sun_xmodmap_pke = < slash / question ?); # naughty chatacrers (some of these generate warnings) @Alias{"parenleft","parenright","space"} = ("(",")"," "); @Alias{"Tab","Return","numbersign","comma"} = ("\t","\n","#",","); # # Populate KeyCode aliase # foreach $line (split(/\n/,$sun_xmodmap_pke)) { ($junk,$code,$junk,$char1,$char2) = split(' ',$line); if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; } if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; } if (length($char1) > 1) { $char1 = "."; } if (length($char2) > 1) { $char2 = "."; } $KeyCode{sun}{0}{$code} = $char1; $KeyCode{sun}{1}{$code} = $char2; } foreach $line (split(/\n/,$linux_xmodmap_pke)) { ($junk,$code,$junk,$char1,$char2) = split(' ',$line); if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; } if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; } if (length($char1) > 1) { $char1 = "."; } if (length($char2) > 1) { $char2 = "."; } $KeyCode{linux}{0}{$code} = $char1; $KeyCode{linux}{1}{$code} = $char2; } } # Set_VNC_Codes - set globals for VNC. # sub Set_VNC_Codes { ### set client code to request size hash. %VNC_Code_Size = ( 0 => 20, 1 => 6, 2 => 4, 3 => 10, 4 => 8, 5 => 6, 6 => 8 ); ### Some essential keysyms $KeyCode{vnc}{0}{"\010"} = "\b"; $KeyCode{vnc}{0}{"\011"} = "\t"; $KeyCode{vnc}{0}{"\015"} = "\n"; } # Touch_Vars - This is stops perl -w warnings about vars used only once. # Part of my todo list is to cull this list. # # sub Touch_Vars { # # Perl < 5.6 code # #use vars qw($ip_ttl $udp_checksum $ip_ident $tcp_length_data #$ip_tos $tcp_options $opt_A $opt_D $tcp_header_rest $opt_J #$opt_P $opt_U $opt_X $opt_e $opt_h $opt_i $pad $opt_j #$snoop_length_orig $http_header $opt_p $opt_q $opt_r #$header_rest $tcp_ack $ether_dest $ether_src $skip #$ip_length $udp_length $ip_options $ip_checksum #$opt_b $opt_B $opt_l $opt_L $ip_rest $ip_hop $ip_reserved #$ip_flow $icmp_rest $opt_f $opt_z); # # Perl 5.6 code # #our ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data, #$ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J, #$opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j, #$snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r, #$header_rest,$tcp_ack,$ether_dest,$ether_src,$skip, #$ip_length,$udp_length,$ip_options,$ip_checksum, #$opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved, #$ip_flow,$icmp_rest,$opt_f,$opt_z); # # Perl < 5.6 and 5.6 code (but not elegant) # @Once_is_okay = ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data, $ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J, $opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j, $snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r, $header_rest,$tcp_ack,$ether_dest,$ether_src,$skip, $ip_length,$udp_length,$ip_options,$ip_checksum,$tcp_rst,$tcp_fin, $opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved, $ip_flow,$icmp_rest,$opt_f,$opt_z,$junk1,$opt_H,$opt_I,$opt_R); } # Check_Command - check which is the network sniffing command and save # it to $command. # sub Check_Command { # # Check which OS we are on, die if it looks incompatible # if ($^O eq "linux") { # # The "-s9999" tells tcpdump to keep a packet up to this # size, otherwise the default is 68 bytes. Some versions of # tcpdump allow using "-s0" for unlimited. # $command = "tcpdump -s9999 -w"; } elsif ($^O eq "solaris") { $command = "snoop -o"; } else { die "ERROR54: Can't find the sniffer command for \"$^O\".\n" . "\t Please use log mode instead.\n"; } # # Check username # if ($ENV{LOGNAME} ne "root") { print STDERR "WARNING: Are you root? If not, this probably " . "won't work. Trying anyway...\n"; } } # Process_Command_Line_Arguments - this process the command line arguments # and sets various globals which are kept in %Arg. It also prints # usage and exists if need be. # sub Process_Command_Line_Arguments { my $result; # # Process Global Defaults into %Arg # foreach (@Save_As_HTML_TCP_Ports) { $Arg{Save_As_TCP_HTML}{$_} = 1; } foreach (@Save_As_HTML_UDP_Ports) { $Arg{Save_As_UDP_HTML}{$_} = 1; } foreach (@Save_As_TCP_Playback_Ports) { $Arg{Save_As_TCP_Playback}{$_} = 1; } foreach (@Save_As_UDP_Playback_Ports) { $Arg{Save_As_UDP_Playback}{$_} = 1; } foreach (@Save_As_X11_Playback_Ports) { $Arg{Save_As_X11_Playback}{$_} = 1; } foreach (@Save_As_HTML_X11_Ports) { $Arg{Save_As_X11_HTML}{$_} = 1; } foreach (@Save_As_VNC_Playback_Ports) { $Arg{Save_As_VNC_Playback}{$_} = 1; } if (defined $ARGV[0]) { ### Dump full help if asked &Usage_Full if $ARGV[0] eq "--help"; ### Dump massive help if asked &Usage_Massive if $ARGV[0] eq "--help2"; } # # Command Line Defaults # $Arg{output_raw} = 0; $Arg{output_hex} = 0; $Arg{output_UDP} = 1; $Arg{output_TCP} = 1; $Arg{output_ICMP} = 1; $Arg{output_info} = 0; $Arg{output_apps} = 1; $Arg{output_index} = 1; $Arg{keydata} = 0; $Arg{debug} = 0; # # Check correct switches were used # Getopt::Long::Configure ("bundling"); $result = GetOptions ( "application!" => \$opt_a, "a" => \$opt_a, "e|everything" => \$opt_e, "h" => \$opt_h, "info!" => \$opt_i, "i" => \$opt_i, "q|quiet" => \$opt_q, "raw!" => \$opt_r, "r" => \$opt_r, "v|verbose" => \$opt_v, "index!" => \$opt_x, "x" => \$opt_x, "A" => \$opt_A, "H|hex" => \$opt_H, "I" => \$opt_I, "R" => \$opt_R, "U|noudp" => \$opt_U, "T|notcp" => \$opt_T, "Y|noicmp" => \$opt_Y, "X" => \$opt_X, "D|dir=s" => \$opt_D, "b|playtcp=s" => \$opt_b, "B|playudp=s" => \$opt_B, "l|htmltcp=s" => \$opt_l, "L|htmludp=s" => \$opt_L, "m|min=s" => \$opt_m, "M|max=s" => \$opt_M, "o|sort=s" => \$opt_o, "p|port=s" => \$opt_p, "P|noport=s" => \$opt_P, "j|ipaddr=s" => \$opt_j, "J|noipaddr=s" => \$opt_J, "s|runonce=s" => \$opt_s, "S|runmany=s" => \$opt_S, "z|runredo" => \$opt_z, "f|filter=s" => \$opt_f, "k|keydata" => \$opt_k, "debug" => \$opt_debug, "bench" => \$opt_bench ); # # Process switches # &Usage() if ($opt_h || ! $result); $Arg{output_raw} = 1 if $opt_r or $opt_v; $Arg{output_hex} = 1 if $opt_H or $opt_e; $Arg{output_info} = 1 if $opt_i or $opt_v; $Arg{quiet} = 1 if $opt_q; $Arg{output_UDP} = 0 if $opt_U; $Arg{output_TCP} = 0 if $opt_T; $Arg{output_ICMP} = 0 if $opt_Y; $Arg{output_apps} = 0 if ($opt_A || (defined $opt_a && $opt_a eq "0")); $Arg{output_index} = 0 if ($opt_X || (defined $opt_x && $opt_x eq "0")); $Arg{output_allhtml} = 1 if $opt_e; my $extra_TCPplayback = $opt_b; my $extra_UDPplayback = $opt_B; my $extra_TCPhtml = $opt_l; my $extra_UDPhtml = $opt_L; my $ports_accepted = $opt_p; my $ports_rejected = $opt_P; my $ips_accepted = $opt_j; my $ips_rejected = $opt_J; $Arg{output_dir} = $opt_D; $Arg{filter} = $opt_f || ""; $Arg{minbytes} = 0; $Arg{maxbytes} = 0; $Arg{sort} = "time"; $Arg{keydata} = 1 if $opt_k; $Arg{debug} = 1 if $opt_debug; $Arg{bench} = 1 if $opt_bench; # # Check for min/max bytes # if (defined $opt_m) { if ($opt_m =~ /k$/) { $opt_m =~ s/k$//; $opt_m *= 1024; } $Arg{minbytes} = $opt_m; } if (defined $opt_M) { if ($opt_M =~ /k$/) { $opt_M =~ s/k$//; $opt_M *= 1024; } $Arg{maxbytes} = $opt_M; } # # Check for sort option # if (defined $opt_o) { if ($opt_o !~ /^(time|size|type|ip)$/) { print STDERR "ERROR55: Sort must be \"time\", " . "\"size\", \"type\" or \"ip\".\n"; &Usage(); } $Arg{sort} = $opt_o; } # # Check for standalone redo mode # if (defined $opt_z) { $Arg{redo} = 1; if (defined $Arg{output_dir}) { # bad luck die "ERROR56: Can't use an output dir " . "$Arg{output_dir} in redo mode.\n\n"; } } # # Check for standalone mode # elsif (defined $opt_s || defined $opt_S) { $Arg{standalone} = 1; if (defined $opt_s) { if ($opt_s =~ /,/) { die "ERROR57: Unexpected comma found in " . "\"-s$opt_s\" (did you mean \"-S$opt_s\"?)\n"; } $Arg{mins} = $opt_s; $Arg{count} = 1; } elsif (defined $opt_S) { my ($mins,$count) = split(/,/,$opt_S); $Arg{mins} = $mins; ### -1 means endless $Arg{count} = $count || -1; } } # # This is normal mode # else { $Arg{normal} = 1; } # # Build accepted or rejected port list as %Arg{Port_Accepted},... # if (defined $ports_accepted) { $Arg{port_accept} = 1; foreach $port (split(/,/,$ports_accepted)) { $Arg{Port_Accepted}{$port} = 1; } } if (defined $ports_rejected) { $Arg{port_reject} = 1; foreach $port (split(/,/,$ports_rejected)) { $Arg{Port_Rejected}{$port} = 1; } } # # Build accepted or rejected IP list as %Arg{IP_Accepted},... # if (defined $ips_accepted) { $Arg{ip_accept} = 1; foreach $ip (split(/,/,$ips_accepted)) { $Arg{IP_Accepted}{$ip} = 1; } } if (defined $ips_rejected) { $Arg{ip_reject} = 1; foreach $ip (split(/,/,$ips_rejected)) { $Arg{IP_Rejected}{$ip} = 1; } } # # Add extra ports to playback or HTML # if (defined $extra_TCPplayback) { foreach $port (split(/,/,$extra_TCPplayback)) { $Arg{Save_As_TCP_Playback}{$port} = 1; } } if (defined $extra_UDPplayback) { foreach $port (split(/,/,$extra_UDPplayback)) { $Arg{Save_As_UDP_Playback}{$port} = 1; } } if (defined $extra_TCPhtml) { foreach $port (split(/,/,$extra_TCPhtml)) { $Arg{Save_As_TCP_HTML}{$port} = 1; } } if (defined $extra_UDPhtml) { foreach $port (split(/,/,$extra_UDPhtml)) { $Arg{Save_As_UDP_HTML}{$port} = 1; } } # # Check infile was provided, or print usage # if (! defined $ARGV[0] && ! ($Arg{standalone} || $Arg{redo})) { &Usage(); } @{$Arg{infiles}} = @ARGV; } # Usage - print command usage and exit. # sub Usage { print "USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir] [-b port[,...]] [-B port[,...]] [-j IPaddr[,...]] [-J IPaddr[,...]] [-l port[,...]] [-L port[,...]] [-m bytes[k]] [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"] [-p port[,...]] [-P port[,...]] infile [infile2 ...] chaosreader -s [mins] | -S [mins[,count]] [-z] [-f 'filter'] eg, chaosreader infile # Create application session files, indexes chaosreader -v infile # Verbose - Create ALL files chaosreader -i infile # Create info files chaosreader -r infile # Create raw files chaosreader -S 2,5 # Standalone - sniff network 5 times by 2 mins. chaosreader -h # Print a brief help (this) chaosreader --help # Print verbose help and version chaosreader --help2 # Print massive help\n\n"; exit(0); } # Usage Full - print command usage and exit. # sub Usage_Full { print "Version 0.94, 01-May-2004 USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir] [-b port[,...]] [-B port[,...]] [-j IPaddr[,...]] [-J IPaddr[,...]] [-l port[,...]] [-L port[,...]] [-m bytes[k]] [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"] [-p port[,...]] [-P port[,...]] infile [infile2 ...] chaosreader -s [mins] | -S [mins[,count]] [-z] [-f 'filter'] chaosreader # Create application session files, indexes -a, --application # Create application session files (default) -e, --everything # Create HTML 2-way & hex files for everything -h # Print a brief help --help # Print verbose help (this) and version --help2 # Print massive help -i, --info # Create info file -q, --quiet # Quiet, no output to screen -r, --raw # Create raw files -v, --verbose # Verbose - Create ALL files .. (except -e) -x, --index # Create index files (default) -A, --noapplication # Exclude application session files -H, --hex # Include hex dumps (slow) -I, --noinfo # Exclude info files -R, --noraw # Exclude raw files -T, --notcp # Exclude TCP traffic -U, --noudp # Exclude UDP traffic -Y, --noicmp # Exclude ICMP traffic -X, --noindex # Exclude index files -k, --keydata # Create extra files for keystroke analysis -D dir --dir dir # Output all files to this directory -b 25,79 --playtcp 25,79 # replay these TCP ports as well (playback) -B 36,42 --playudp 36,42 # replay these UDP ports as well (playback) -l 7,79 --htmltcp 7,79 # Create HTML for these TCP ports as well -L 7,123 --htmludp 7,123 # Create HTML for these UDP ports as well -m 1k --min 1k # Min size of connection to save (\"k\" for Kb) -M 1024k --max 1k # Max size of connection to save (\"k\" for Kb) -o size --sort size # sort Order: time/size/type/ip (Default time) -p 21,23 --port 21,23 # Only examine these ports (TCP & UDP) -P 80,81 --noport 80,81 # Exclude these ports (TCP & UDP) -s 5 --runonce 5 # Standalone. Run tcpdump/snoop for 5 mins. -S 5,10 --runmany 5,10 # Standalone, many. 10 samples of 5 mins each. -S 5 --runmany 5 # Standalone, endless. 5 min samples forever. -z --runredo # Standalone, redo. Rereads last run's logs. -j 10.1.2.1 --ipaddr 10.1.2.1 # Only examine these IPs -J 10.1.2.1 --noipaddr 10.1.2.1 # Exclude these IPs -f 'port 7' --filter 'port 7' # With standalone, use this dump filter. eg1, tcpdump -s9000 -w output1 # create tcpdump capture file chaosreader output1 # extract recognised sessions, or, chaosreader -ve output1 # gimme everything, or, chaosreader -p 20,21,23 output1 # only ftp and telnet... eg2, snoop -o output1 # create snoop capture file instead chaosreader output1 # extract recognised sessions... eg3, chaosreader -S 2,5 # Standalone, sniff network 5 times for 2 mins # each. View index.html for progress (or .text) "; exit(0); } # Usage_Massive - print massive help. Actually strip it from the comments # at the top of the code. # sub Usage_Massive { open (MYSELF,"$0") || die "ERROR58: I can't see myself: $!\n"; @Myself = ; close MYSELF; ### Print comment from top of code foreach $line (@Myself) { last if $line !~ /^#/; last if $line =~ /^# Todo:/; next if $line =~ m:^#!/usr/bin/perl:; $line =~ s/^#/ /; print $line; } print "\n"; exit(0); } __END__ Reminders for myself ==================== /s for multiline match Comments style: # Micro comment ### Tiny Comment # # Small comment # # # --- Meduim Comment --- # ######################### # --- Large Comment --- # ######################## # --- Huge Comment --- # ######################## Error message style =================== die "ERROR#: message: $!\n"; Data types, =========== %Arg -> @infiles -> output_raw -> output_hex -> output_UDP -> output_info -> output_apps -> output_index -> output_allhtml -> Save_As_TCP_HTML -> $port -> Save_As_UDP_HTML -> $port -> Save_As_TCP_Playback -> $port -> Save_As_UDP_Playback -> $port -> Port_Accepted -> $port -> Port_Rejected -> $port -> ip_accept -> ip_reject -> IP_Accepted -> $ip -> IP_Rejected -> $ip -> debug -> standalone -> redo -> normal -> mins -> count -> output_dir -> quiet -> infile -> minbytes -> maxbytes %IP -> time -> $packet_time -> ver -> src -> dest -> protocol -> frag -> $ip_frag -> fragged -> drops -> id -> $ip_id -> StartTime %TCP -> id -> $session_id -> src -> dest -> source # SYN seen -> src_port -> dest_port -> Aseq -> $$tcp_seq -> Bseq -> $$tcp_seq -> time -> $time -> dir -> data -> BothHTML -> StartTime -> EndTime -> size -> knowndir %UDP -> id -> $session_id -> src -> dest -> src_port -> dest_port -> RawA -> RawB -> time -> $time -> BothHTML -> StartTime -> EndTime -> size %ICMP -> time -> type -> code -> src -> dest -> Partial -> ver -> size %Count -> IP -> IPprotocols -> TCPports -> UDPports -> EtherType %CountMaster (as above) %Index -> @HTML -> @Text -> Time_Order -> $session_timeid -> Sort_Lookup -> $session_timeid %Image -> @HTML -> links -> info -> notempty %GETPOST -> @HTML -> query -> info -> notempty %Hex -> $type -> $session_id -> offset -> pos -> hextext -> hexhtml -> viewtext -> viewhtml %Filenames -> $time -> filename -> service -> session_id @Master -> starttime -> endtime -> duration -> size -> dir -> file
    $type" . "$CountMaster{EtherType}{$type}