diff --git a/chaosreader.spec b/chaosreader.spec index 9c1e657..7802b03 100644 --- a/chaosreader.spec +++ b/chaosreader.spec @@ -1,15 +1,20 @@ Name: chaosreader -Version: 0.94 -Release: 2mamba +Version: 0.96 +Release: 1mamba Summary: An open source tool to trace TCP/UDP/... sessions and fetch application data Group: Network/Monitoring Vendor: openmamba Distribution: openmamba Packager: Stefano Cotta Ramusino -URL: http://www.brendangregg.com/chaosreader.html -Source: http://downloads.sourceforge.net/chaosreader/chaosreader%{version} +URL: https://github.com/brendangregg/Chaosreader +Source: https://github.com/brendangregg/Chaosreader.git/%{version}/Chaosreader-%{version}.tar.bz2 +#Source: http://downloads.sourceforge.net/chaosreader/chaosreader%{version} License: GPL BuildArch: noarch +## AUTOBUILDREQ-BEGIN +BuildRequires: perl-Net-DNS +BuildRequires: perl-devel +## AUTOBUILDREQ-END BuildRoot: %{_tmppath}/%{name}-%{version}-root %description @@ -19,15 +24,16 @@ A html index file is created that links to all the session details, including re 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. %prep +%setup -q -n Chaosreader-%{version} %build %install [ "%{buildroot}" != / ] && rm -rf "%{buildroot}" -install -D -m 755 %{S:0} \ - %{buildroot}%{_bindir}/%{name} +install -D -m 755 chaosreader \ + %{buildroot}%{_bindir}/chaosreader -%{buildroot}%{_bindir}/%{name} --help2 > README +#%{buildroot}%{_bindir}/%{name} --help2 > README %clean [ "%{buildroot}" != / ] && rm -rf "%{buildroot}" @@ -35,9 +41,12 @@ install -D -m 755 %{S:0} \ %files %defattr(-,root,root) %{_bindir}/%{name} -%doc README +%doc README.md %changelog +* Wed Feb 10 2021 Silvan Calarco 0.96-1mamba +- update to 0.96 + * Thu Sep 05 2013 Automatic Build System 0.94-2mamba - automatic rebuild by autodist diff --git a/chaosreader0.94 b/chaosreader0.94 deleted file mode 100644 index 0f19145..0000000 --- a/chaosreader0.94 +++ /dev/null @@ -1,6832 +0,0 @@ -#!/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}