6833 lines
182 KiB
Perl
Executable File
6833 lines
182 KiB
Perl
Executable File
#!/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} .=
|
|
"<font color=\"blue\">$copy</font>";
|
|
|
|
#
|
|
# 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} .=
|
|
"<font color=\"red\">$copy</font>";
|
|
#
|
|
# 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} .=
|
|
"<font color=\"blue\">$copy</font>";
|
|
}
|
|
|
|
#
|
|
# 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] = "<tr><td><i>$number.</i></td>" .
|
|
"<td><b>$starttime</b></td><td>$duration s</td><td> " .
|
|
"<font color=\"blue\">$id_html " .
|
|
"</font></td><td> <font color=\"red\">" .
|
|
"$service_name</font></td><td> <font color=\"green\"> " .
|
|
"$length bytes</font></td><td>\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} = "<tr><td><i>$number.</i>" .
|
|
"</td><td><b>$starttime</b></td><td> " .
|
|
"<font color=\"blue\">$id_html </font></td><td><td>\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} = "<tr><td><i>$number.</i>" .
|
|
"</td><td><b>$starttime</b></td><td> " .
|
|
"<font color=\"blue\">$id_html </font></td><td><td>\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] .= "<li><a href=\"$filename\">raw</a> ";
|
|
|
|
#
|
|
# 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] .= "<a href=\"$filename\">raw1</a> ";
|
|
|
|
#
|
|
# 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] .= "<a href=\"$filename\">raw2</a></li> ";
|
|
}
|
|
|
|
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] = "<tr><td><i>$number.</i></td>" .
|
|
"<td><b>$starttime</b></td><td>$duration s</td><td> " .
|
|
"<font color=\"blue\">$id_html " .
|
|
"</font></td><td> <font color=\"red\">" .
|
|
"<i>$service_name</i></font></td><td> <font color=\"green\"> " .
|
|
"$length bytes</font></td><td>\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] .= "<li><a href=\"$filename\">raw</a> ";
|
|
|
|
#
|
|
# 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] .= "<a href=\"$filename\">raw1</a> ";
|
|
|
|
#
|
|
# 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] .= "<a href=\"$filename\">raw2</a></li> ";
|
|
}
|
|
|
|
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] = "<tr><td><i>$number.</i></td>" .
|
|
"<td><b>$starttime</b></td><td>0 s</td><td> " .
|
|
"<font color=\"blue\">$id_html" .
|
|
"</font></td><td> <font color=\"red\">" .
|
|
"<i>$icmp_ver</i></font></td><td> <font color=\"green\"> " .
|
|
"$length bytes</font></td><td>$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] .= "<li><a href=\"$filename\">raw</a> ";
|
|
|
|
}
|
|
|
|
#
|
|
# --- 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} .=
|
|
"<font color=\"red\">GET</font></td><td width=70%>";
|
|
$GETPOST{notempty} = 1;
|
|
} else {
|
|
$GETPOST{HTML}[$number]{query} .= "<hr>\n";
|
|
}
|
|
|
|
#
|
|
# Generate table of query key value pairs
|
|
#
|
|
$GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\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/>/>/g;
|
|
$value =~ s/\n/<br>\n/g;
|
|
$GETPOST{HTML}[$number]{query} .=
|
|
"<tr><td><b>$var</b></td>" .
|
|
"<td><font face=\"Courier\">$value</font></td></tr>\n";
|
|
}
|
|
$GETPOST{HTML}[$number]{query} .= "</table>\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} .=
|
|
"<font color=\"red\">POST</font></td><td width=70%>";
|
|
$GETPOST{notempty} = 1;
|
|
} else {
|
|
$GETPOST{HTML}[$number]{query} .= "<hr>\n";
|
|
}
|
|
|
|
($site) = $request =~ /^POST (\S*)\s/;
|
|
|
|
$post =~ s/HTTP .*//s;
|
|
|
|
#
|
|
# Generate table of query key value pairs
|
|
#
|
|
$GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\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/>/>/g;
|
|
$value =~ s/\n/<br>/g;
|
|
$GETPOST{HTML}[$number]{query} .=
|
|
"<tr><td><b>$var</b></td>" .
|
|
"<td><font face=\"Courier\">$value</font></td></tr>\n";
|
|
}
|
|
$GETPOST{HTML}[$number]{query} .= "</table>\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 <<END_HTML;
|
|
<html>
|
|
<head><title>Chaosreader Report, $Arg{infile}</title></head>
|
|
<body bgcolor="white" textcolor="black">
|
|
<font size=+3>Chaosreader Report</font><br>
|
|
<font size=+1>File: $Arg{infile}, Type: $TYPE, Created at: $the_date</font><p>
|
|
<a href="image.html"><font color="blue"><b>Image Report</b></font></a>
|
|
$image_empty - Click here for a report on captured images.<br>
|
|
<a href="getpost.html"><font color="blue"><b>GET/POST Report</b></font></a>
|
|
$getpost_empty - Click here for a report on HTTP GETs and POSTs.<br>
|
|
<a href="httplog.text"><font color="blue"><b>HTTP Proxy Log</b></font></a>
|
|
$httplog_empty - Click here for a generated proxy style HTTP log.<p>
|
|
<font size=+2>TCP/UDP/... Sessions</font><br>
|
|
<table border=2>
|
|
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 </td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>IP Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $IP (sort {$Count{IP}{$b} <=> $Count{IP}{$a}}
|
|
keys %{$Count{IP}}) {
|
|
print FILE "<tr><td>$IP</td><td>$Count{IP}{$IP}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>TCP Port Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $port (sort {$Count{TCPport}{$b} <=> $Count{TCPport}{$a}}
|
|
keys %{$Count{TCPport}}) {
|
|
$port_text = $Services_TCP{$port} || $port || "0";
|
|
print FILE "<tr><td>$port_text</td><td>$Count{TCPport}{$port}" .
|
|
"</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>UDP Port Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $port (sort {$Count{UDPport}{$b} <=> $Count{UDPport}{$a}}
|
|
keys %{$Count{UDPport}}) {
|
|
$port_text = $Services_UDP{$port} || $port || "0";
|
|
print FILE "<tr><td>$port_text</td><td>$Count{UDPport}{$port}" .
|
|
"</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>IP Protocol Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $protocol (sort {$Count{IPprotocol}{$b} <=>
|
|
$Count{IPprotocol}{$a}} keys %{$Count{IPprotocol}}) {
|
|
$protocol_text = $IP_Protocols{$protocol};
|
|
print FILE "<tr><td>$protocol_text</td><td>" .
|
|
"$Count{IPprotocol}{$protocol}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>Ethernet Type Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $type (sort {$Count{EtherType}{$b} <=> $Count{EtherType}{$a}}
|
|
keys %{$Count{EtherType}}) {
|
|
print FILE "<tr><td>$type</td><td>$Count{EtherType}{$type}" .
|
|
"</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table>
|
|
</body>
|
|
</html>
|
|
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 <<END_HTML;
|
|
<html>
|
|
<head><title>Chaosreader Image Report</title></head>
|
|
<body bgcolor="white" textcolor="black">
|
|
<font size=+3>Chaosreader Image Report</font><br>
|
|
<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
|
|
<font size=+2>Images</font><br>
|
|
<table border=2>
|
|
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 </td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
</body>
|
|
</html>
|
|
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 <<END_HTML;
|
|
<html>
|
|
<head><title>Chaosreader GET/POST Report</title></head>
|
|
<body bgcolor="white" textcolor="black">
|
|
<font size=+3>Chaosreader GET/POST Report</font><br>
|
|
<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
|
|
<font size=+2>HTTP GETs and POSTs</font><br>
|
|
<table border=2>
|
|
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 </td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
</body>
|
|
</html>
|
|
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 <<END_HTML;
|
|
<html>
|
|
<head><title>Chaosreader Master Index</title></head>
|
|
<body bgcolor="white" textcolor="black" vlink="blue">
|
|
<font size=+3>Chaosreader Master Index</font><br>
|
|
<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
|
|
<a href="$recentname/index.html"><font color="red">
|
|
<b>Most Recent Report</b></font></a>
|
|
- Click here for the most recent index, and click reload for updates.<p>
|
|
<font size=+2>Chaosreader Reports</font><br>
|
|
<table border=2>
|
|
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 = "<tr><td><i>". ($index+1) . "</i></td>" .
|
|
"<td><b>$start</b></td><td><b>$end</b></td>\n" .
|
|
"<td>$duration s</td>" . "<td><font color=\"green\"> " .
|
|
"$size bytes</font></td>" .
|
|
"<td><a href=\"$dir/index.html\">$dir/$file</a></td></tr>\n";
|
|
print FILE "$html_line </td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>IP Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $IP (sort {$CountMaster{IP}{$b} <=> $CountMaster{IP}{$a}}
|
|
keys %{$CountMaster{IP}}) {
|
|
print FILE "<tr><td>$IP</td><td>$CountMaster{IP}{$IP}" .
|
|
"</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>TCP Port Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $port (sort {$CountMaster{TCPport}{$b} <=>
|
|
$CountMaster{TCPport}{$a}} keys %{$CountMaster{TCPport}}) {
|
|
$port_text = $Services_TCP{$port} || $port || "0";
|
|
print FILE "<tr><td>$port_text</td><td>" .
|
|
"$CountMaster{TCPport}{$port}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>UDP Port Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $port (sort {$CountMaster{UDPport}{$b} <=>
|
|
$CountMaster{UDPport}{$a}} keys %{$CountMaster{UDPport}}) {
|
|
$port_text = $Services_UDP{$port} || $port || "0";
|
|
print FILE "<tr><td>$port_text</td><td>" .
|
|
"$CountMaster{UDPport}{$port}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>IP Protocol Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $protocol (sort {$CountMaster{IPprotocol}{$b} <=>
|
|
$CountMaster{IPprotocol}{$a}} keys %{$CountMaster{IPprotocol}}) {
|
|
$protocol_text = $IP_Protocols{$protocol};
|
|
print FILE "<tr><td>$protocol_text</td><td>" .
|
|
"$CountMaster{IPprotocol}{$protocol}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table><p>
|
|
<font size=+2>Ethernet Type Count</font><br>
|
|
<table border=2>
|
|
END_HTML
|
|
foreach $type (sort {$CountMaster{EtherType}{$b} <=>
|
|
$CountMaster{EtherType}{$a}} keys %{$CountMaster{EtherType}}) {
|
|
print FILE "<tr><td>$type</td><td>" .
|
|
"$CountMaster{EtherType}{$type}</td></tr>\n";
|
|
}
|
|
print FILE <<END_HTML;
|
|
</table>
|
|
</body>
|
|
</html>
|
|
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 =~ /<html>/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;
|
|
$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</font color="red"></g;
|
|
$data =~ tr/\040-\176\n\r\f/./c; # max 376, was 245
|
|
if (defined $plain) {
|
|
# This is a plain style of line wrap
|
|
$data =~ s/([^\n\f<>]{$WRAP})/$&\n/g;
|
|
} else {
|
|
# This is a fancy line wrap, a green ">" starts the wrapped lines
|
|
$data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/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</font color="red"></g;
|
|
$data =~ tr/\040-\176\n\r\f/./c; # max 376, was 245
|
|
if (defined $plain) {
|
|
# This is a plain style of line wrap
|
|
$data =~ s/([^\n\f<>]{$WRAP})/$&\n/g;
|
|
} else {
|
|
# This is a fancy line wrap, a green ">" starts the wrapped lines
|
|
$data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/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 .= "<font color=\"$colour\">";
|
|
$viewhtml .= "<font color=\"$colour\">";
|
|
|
|
### Process
|
|
@Bytes = unpack("C*",$data);
|
|
foreach $byte (@Bytes) {
|
|
$view = chr($byte);
|
|
$view =~ tr/\040-\176/./c;
|
|
$view2 = $view;
|
|
$view2 =~ s/</</g;
|
|
$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} .=
|
|
'<font color="green">' . sprintf("%6.08x",$offset) .
|
|
"</font> $hexhtml $viewhtml\n";
|
|
|
|
$pos = 1;
|
|
$offset += 16;
|
|
$hexhtml = "</font><font color=\"$colour\">";
|
|
$viewhtml = "</font><font color=\"$colour\">";
|
|
$hextext = $viewtext = "";
|
|
}
|
|
if ( ($pos != 1) && (($pos %2) == 1) ) {
|
|
$hexhtml .= " ";
|
|
$hextext .= " ";
|
|
}
|
|
}
|
|
$hexhtml .= "</font>";
|
|
$viewhtml .= "</font>";
|
|
|
|
$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} .=
|
|
'<font color="green">' . sprintf("%6.08x",$offset) .
|
|
"</font> $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 .=
|
|
"</font><font color=\"$colour\">$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 .= "<font color=\"blue\">$raw</font>";
|
|
} else {
|
|
$session_data .= "<font color=\"red\">$raw</font>";
|
|
}
|
|
}
|
|
$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 "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" .
|
|
"<BODY bgcolor=\"white\">\n" .
|
|
"<H1>$service_name: $session_text</H1>\n" .
|
|
"<H2>File $Arg{infile}, Session $number</H2>\n" .
|
|
"<PRE WRAP=\"virtual\">\n" .
|
|
$session_data . "</PRE>\n</BODY>\n</HTML>\n";
|
|
close OUT;
|
|
|
|
### Global Vars
|
|
my $length = length($session_data);
|
|
$Index{HTML}[$number] .= "<li><a href=\"$filename\">as_html</a></li>\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 "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" .
|
|
"<BODY bgcolor=\"white\">\n" .
|
|
"<H1>$service_name: $session_text</H1>\n" .
|
|
"<H2>File $Arg{infile}, Session $number</H2>\n" .
|
|
"<PRE WRAP=\"virtual\">\n" .
|
|
$session_data . "</PRE>\n</BODY>\n</HTML>\n";
|
|
close OUT;
|
|
|
|
### Global Vars
|
|
my $length = length($session_data);
|
|
$Index{HTML}[$number] .= "<li>";
|
|
$Index{HTML}[$number] .= "<a href=\"$filename\">hex</a></li>\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] .=
|
|
"<li><a href=\"$filename\">$filename</a> $length bytes</li>\n";
|
|
$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
|
|
'"' , " $filename","",$length);
|
|
if (&Is_Image($ftp_type)) {
|
|
$Image{HTML}[$number]{links} .=
|
|
"<img src=\"$filename\"> ";
|
|
$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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $duration seconds</li>\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 .= '<font color="blue">' . $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 color="green">><\/font>/g;
|
|
$html .= '<font color="blue">' . $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 .= '</font>';
|
|
}
|
|
|
|
#
|
|
# --- 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 .= '<font color="red">' . $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 color="green">><\/font>/g;
|
|
$html .= '<font color="red">' . $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 .= '</font>';
|
|
|
|
### 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 = "<html><head><title>SSH text analysis</title></head>\n" .
|
|
"<body bgcolor=\"white\">" .
|
|
"<H1>$service_name: $session_text</H1>\n" .
|
|
"<H2>File $Arg{infile}, Session $number</H2>\n" .
|
|
"<h3>$duration2 seconds, $bytes bytes, $speed Kb/sec</h3>\n" .
|
|
'"*" is client traffic (including ' .
|
|
'keystrokes), "." is the return ' .
|
|
'text.<br>A number is a multiple of the previous char, eg ".32" ' .
|
|
'is 32 return chars.<br>' .
|
|
"\n<b><pre>$html</pre></b>\n</body>\n</html>\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] .= "<li><a href=\"$filename1\">$filename1" .
|
|
"</a> $duration seconds</li>\n";
|
|
$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
|
|
'"' , " $filename1","",$duration);
|
|
$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
|
|
"</a> </li>\n";
|
|
$length = length($html);
|
|
$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
|
|
'"' , " $filename2","",$length);
|
|
$Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" .
|
|
"</a> </li>\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] .= "<li><a href=\"$filename3\">$filename3" .
|
|
"</a> </li>\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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $duration seconds</li>\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 = <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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $duration seconds</li>\n";
|
|
$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
|
|
"</a> $duration seconds</li>\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 "<HTML>\n<BODY bgcolor=\"white\">\n" .
|
|
"<H1>$service_name: $session_text</H1>\n" .
|
|
"<H2>File $Arg{infile}, Session $number</H2>\n" .
|
|
"<PRE WRAP=\"virtual\">\n" .
|
|
"<font color=\"red\">" .$html. "</font></PRE>\n</BODY>\n</HTML>\n";
|
|
close OUT;
|
|
|
|
### Global Vars
|
|
$length = length($html);
|
|
$Index{HTML}[$number] .=
|
|
"<li><a href=\"$filename3\">keystrokes</a></li>\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 <<WELCOME;
|
|
Port opened successfully.
|
|
|
|
Now run vncviewer and connect to this port. eg,
|
|
vncviewer -viewonly localhost:$vncport
|
|
|
|
If you are prompted for a password, type any character and hit enter.
|
|
Waiting for connection...
|
|
WELCOME
|
|
|
|
|
|
# --- 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 "ERROR49: I can't see myself: $!\n";
|
|
@Myself = <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);
|
|
}
|
|
|
|
|
|
#
|
|
# --- 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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $duration seconds</li>\n";
|
|
$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
|
|
"</a> $duration seconds</li>\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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $length bytes</li>\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] .= "<li><a href=\"$filename\">$filename" .
|
|
"</a> $length bytes</li>\n";
|
|
$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
|
|
'"' , " $filename","",$length);
|
|
if (&Is_Image($http_type)) {
|
|
$Image{HTML}[$number]{links} .=
|
|
"<img src=\"$filename\"> ";
|
|
$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] .= "<li><a href=\"$filename\">$filename</a>" .
|
|
" $length bytes</li>\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 = <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 = <SERVICES>) {
|
|
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 = <<END;
|
|
keycode 8 = Control_L
|
|
keycode 9 = Control_R
|
|
keycode 10 = Shift_L
|
|
keycode 11 = Shift_R
|
|
keycode 12 = Meta_L
|
|
keycode 13 = Meta_R
|
|
keycode 14 = Alt_L
|
|
keycode 15 = Alt_R
|
|
keycode 16 = space
|
|
keycode 17 = 0 parenright
|
|
keycode 18 = 1 exclam
|
|
keycode 19 = 2 at
|
|
keycode 20 = 3 numbersign
|
|
keycode 21 = 4 dollar
|
|
keycode 22 = 5 percent
|
|
keycode 23 = 6 asciicircum
|
|
keycode 24 = 7 ampersand
|
|
keycode 25 = 8 asterisk
|
|
keycode 26 = 9 parenleft
|
|
keycode 27 = minus underscore
|
|
keycode 28 = equal plus
|
|
keycode 29 = bracketleft braceleft
|
|
keycode 30 = bracketright braceright
|
|
keycode 31 = semicolon colon
|
|
keycode 32 = apostrophe quotedbl
|
|
keycode 33 = grave asciitilde
|
|
keycode 34 = comma less
|
|
keycode 35 = period greater
|
|
keycode 36 = slash question
|
|
keycode 37 = backslash bar
|
|
keycode 38 = a A
|
|
keycode 39 = b B
|
|
keycode 40 = c C
|
|
keycode 41 = d D
|
|
keycode 42 = e E
|
|
keycode 43 = f F
|
|
keycode 44 = g G
|
|
keycode 45 = h H
|
|
keycode 46 = i I
|
|
keycode 47 = j J
|
|
keycode 48 = k K
|
|
keycode 49 = l L
|
|
keycode 50 = m M
|
|
keycode 51 = n N
|
|
keycode 52 = o O
|
|
keycode 53 = p P
|
|
keycode 54 = q Q
|
|
keycode 55 = r R
|
|
keycode 56 = s S
|
|
keycode 57 = t T
|
|
keycode 58 = u U
|
|
keycode 59 = v V
|
|
keycode 60 = w W
|
|
keycode 61 = x X
|
|
keycode 62 = y Y
|
|
keycode 63 = z Z
|
|
keycode 64 = BackSpace
|
|
keycode 65 = Return
|
|
keycode 66 = Tab
|
|
keycode 67 = Escape
|
|
keycode 68 = Delete
|
|
END
|
|
|
|
#
|
|
# These are generated using "xmodmap -pke" (and trimmed a little).
|
|
#
|
|
$linux_xmodmap_pke = <<END;
|
|
keycode 8 =
|
|
keycode 9 = Escape
|
|
keycode 10 = 1 exclam
|
|
keycode 11 = 2 at
|
|
keycode 12 = 3 numbersign
|
|
keycode 13 = 4 dollar
|
|
keycode 14 = 5 percent
|
|
keycode 15 = 6 asciicircum
|
|
keycode 16 = 7 ampersand
|
|
keycode 17 = 8 asterisk
|
|
keycode 18 = 9 parenleft
|
|
keycode 19 = 0 parenright
|
|
keycode 20 = minus underscore
|
|
keycode 21 = equal plus
|
|
keycode 22 = BackSpace Terminate_Server
|
|
keycode 23 = Tab ISO_Left_Tab
|
|
keycode 24 = q Q
|
|
keycode 25 = w W
|
|
keycode 26 = e E
|
|
keycode 27 = r R
|
|
keycode 28 = t T
|
|
keycode 29 = y Y
|
|
keycode 30 = u U
|
|
keycode 31 = i I
|
|
keycode 32 = o O
|
|
keycode 33 = p P
|
|
keycode 34 = bracketleft braceleft
|
|
keycode 35 = bracketright braceright
|
|
keycode 36 = Return
|
|
keycode 37 = Control_L
|
|
keycode 38 = a A
|
|
keycode 39 = s S
|
|
keycode 40 = d D
|
|
keycode 41 = f F
|
|
keycode 42 = g G
|
|
keycode 43 = h H
|
|
keycode 44 = j J
|
|
keycode 45 = k K
|
|
keycode 46 = l L
|
|
keycode 47 = semicolon colon
|
|
keycode 48 = apostrophe quotedbl
|
|
keycode 49 = grave asciitilde
|
|
keycode 50 = Shift_L
|
|
keycode 51 = backslash bar
|
|
keycode 52 = z Z
|
|
keycode 53 = x X
|
|
keycode 54 = c C
|
|
keycode 55 = v V
|
|
keycode 56 = b B
|
|
keycode 57 = n N
|
|
keycode 58 = m M
|
|
keycode 59 = comma less
|
|
keycode 60 = period greater
|
|
keycode 61 = slash question
|
|
keycode 62 = Shift_R
|
|
keycode 64 = Alt_L Meta_L
|
|
keycode 65 = space
|
|
keycode 94 = less greater
|
|
END
|
|
%Alias = qw(exclam ! at @ dollar $ percent %
|
|
asciicircum ^ ampersand & asterisk * minus - underscore _
|
|
equal = plus + bracketleft [ bracketright ] braceleft {
|
|
braceright } semicolon ; colon : apostrophe ' quotedbl "
|
|
grave ` asciitilde ~ backslash \ bar | less <
|
|
period . greater > 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 = <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
|
|
|
|
|
|
|
|
|