1#!/usr/local/bin/perl 2# 3# Chaosreader can trace TCP/UDP/... sessions and fetch application data 4# from tcpdump or snoop logs. This is like an "any-snarf" program, it will 5# fetch telnet sessions, FTP files, HTTP transfers (HTML, GIF, JPEG, ...), 6# SMTP emails, etc ... from the captured data inside the network traffic 7# logs. It creates a html index file that links to all the session details, 8# including realtime replay programs for telnet, rlogin or IRC sessions; 9# and reports such as image reports and HTTP GET/POST content reports. 10# It also creates replay programs for telnet sessions, so that you can 11# play them back in realtime (or even different speeds). 12# 13# Chaosreader can also run in standalone mode - where it invokes tcpdump or 14# snoop (if they are available) to create the log files and then processes 15# them. 16# 17# 15-Jun-2014, ver 0.96 https://github.com/brendangregg/Chaosreader 18# 19# Chaosreader currently lives in that github location. If 2014 sounds really 20# old, you may want to run a web search for "chaosreader" in case it's updated 21# somewhere else. Also see http//www.brendangregg.com/chaosreader.html. 22# 23# Recent versions: 24# 25# 11-Sep-2011, ver 0.95 26# 24-Sep 2011, ver 0.95b 27# 04-Jan 2012, ver 0.95c 28# 10-Jan 2012, ver 0.95d 29# 15-Mar 2013, ver 0.95e 30# 15-Apr 2013, ver 0.95f 31# 18-Apr 2013, ver 0.95g 32# 12-Apr 2014, ver 0.95h 33# 14-Apr 2014, ver 0.95i 34# 12-jun 2014, ver 0.95.10 35# 36# QUICK USAGE: 37# tcpdump -s9000 -w out1; chaosreader out1; netscape index.html 38# or, 39# snoop -o out1; chaosreader out1; netscape index.html 40# or, 41# ethereal (save as "out1"); chaosreader out1; netscape index.html 42# or, 43# chaosreader -s 5; netscape index.html 44# 45# 46# USAGE: chaosreader [-adehiknqrvxAHIRTUXY] [-D dir] 47# [-b port[,...]] [-B port[,...]] 48# [-j IPaddr[,...]] [-J IPaddr[,...]] 49# [-l port[,...]] [-L port[,...]] [-m bytes[k]] 50# [-M bytes[k]] [-o "time"|"size"|"type"|"ip"] 51# [-p port[,...]] [-P port[,...]] 52# infile [infile2 ...] 53# 54# chaosreader -s [mins] | -S [mins[,count]] 55# [-z] [-f 'filter'] 56# 57# chaosreader # Create application session files, indexes 58# 59# -a, --application # Create application session files (default) 60# -d, --preferdns # Show DNS names instead of IP addresses 61# -e, --everything # Create HTML 2-way & hex files for everything 62# -h # Print a brief help 63# --help # Print verbose help (this) and version 64# --help2 # Print massive help 65# -i, --info # Create info file 66# -q, --quiet # Quiet, no output to screen 67# -r, --raw # Create raw files 68# -v, --verbose # Verbose - Create ALL files .. (except -e) 69# -x, --index # Create index files (default) 70# -A, --noapplication # Exclude application session files 71# -H, --hex # Include hex dumps (slow) 72# -I, --noinfo # Exclude info files 73# -R, --noraw # Exclude raw files 74# -T, --notcp # Exclude TCP traffic 75# -U, --noudp # Exclude UDP traffic 76# -Y, --noicmp # Exclude ICMP traffic 77# -X, --noindex # Exclude index files 78# -k, --keydata # Create extra files for keystroke analysis 79# -n, --names # Include hostnames in hyperlinked HTTPlog (HTML) 80# -D dir --dir dir # Output all files to this directory 81# -b 25,79 --playtcp 25,79 # replay these TCP ports as well (playback) 82# -B 36,42 --playudp 36,42 # replay these UDP ports as well (playback) 83# -l 7,79 --htmltcp 7,79 # Create HTML for these TCP ports as well 84# -L 7,123 --htmludp 7,123 # Create HTML for these UDP ports as well 85# -m 1k --min 1k # Min size of connection to save ("k" for Kb) 86# -M 1024k --max 1k # Max size of connection to save ("k" for Kb) 87# -o size --sort size # sort Order: time/size/type/ip (Default time) 88# -p 21,23 --port 21,23 # Only examine these ports (TCP & UDP) 89# -P 80,81 --noport 80,81 # Exclude these ports (TCP & UDP) 90# -s 5 --runonce 5 # Standalone. Run tcpdump/snoop for 5 mins. 91# -S 5,10 --runmany 5,10 # Standalone, many. 10 samples of 5 mins each. 92# -S 5 --runmany 5 # Standalone, endless. 5 min samples forever. 93# -z --runredo # Standalone, redo. Rereads last run's logs. 94# -j 10.1.2.1 --ipaddr 10.1.2.1 # Only examine these IPs 95# -J 10.1.2.1 --noipaddr 10.1.2.1 # Exclude these IPs 96# -f 'port 7' --filter 'port 7' # With standalone, use this dump filter. 97# 98# eg1, 99# tcpdump -s9000 -w output1 # create tcpdump capture file 100# chaosreader output1 # extract recognised sessions, or, 101# chaosreader -ve output1 # gimme everything, or, 102# chaosreader -p 20,21,23 output1 # only ftp and telnet... 103# eg2, 104# snoop -o output1 # create snoop capture file instead 105# chaosreader output1 # extract recognised sessions... 106# eg3, 107# chaosreader -S 2,5 # Standalone, sniff network 5 times for 2 mins 108# # each. View index.html for progress (or .text) 109# 110# Output Files: Many will be created, run this in a clean directory. 111# 112# Short example: 113# index.html Html index (full details) 114# index.text Text index 115# index.file File index for standalone redo mode 116# image.html HTML report of images 117# getpost.html HTML report of HTTP GET/POST requests 118# session_0001.info Info file describing TCP session #1 119# session_0001.telnet.html HTML coloured 2-way capture (time sorted) 120# session_0001.telnet.raw Raw data 2-way capture (time sorted) 121# session_0001.telnet.raw1 Raw 1-way capture (assembeled) server->client 122# session_0001.telnet.raw2 Raw 1-way capture (assembeled) client->server 123# session_0002.web.html HTML coloured 2-way 124# session_0002.part_01.html HTTP portion of the above, a HTML file 125# session_0003.web.html HTML coloured 2-way 126# session_0003.part_01.jpeg HTTP portion of the above, a JPEG file 127# session_0004.web.html HTML coloured 2-way 128# session_0004.part_01.gif HTTP portion of the above, a GIF file 129# session_0005.part_01.ftp-data.gz An FTP transfer, a gz file. 130# ... 131# 132# The convention is: 133# session_* TCP Sessions 134# stream_* UDP Streams 135# icmp_* ICMP packets 136# index.html HTML Index 137# index.text Text Index 138# index.file File Index for standalone redo mode only 139# image.html HTML report of images 140# getpost.html HTML report of HTTP GET/POST requests 141# *.info Info file describing the Session/Stream 142# *.raw Raw data 2-way capture (time sorted) 143# *.raw1 Raw 1-way capture (assembeled) server->client 144# *.raw2 Raw 1-way capture (assembeled) client->server 145# *.replay Session replay program (perl) 146# *.partial.* Partial capture (tcpdump/snoop were aware of drops) 147# *.hex.html 2-way Hex dump, rendered in coloured HTML 148# *.hex.text 2-way Hex dump in plain text 149# *.X11.replay X11 replay script (talks X11) 150# *.textX11.replay X11 communicated text replay script (text only) 151# *.textX11.html 2-way text report, rendered in red/blue HTML 152# *.keydata Keystroke delay data file. Used for SSH analysis. 153# 154# Modes: 155# * Normal - eg "chaosreader infile", this is where a tcpdump/snoop file 156# was created previously and chaosreader reads and processes it. 157# * Standalone, once - eg "chaosreader -s 10", this is where chaosreader 158# runs tcpdump/snoop and generates the log file, in this case for 10 i 159# minutes, and then processes the result. Some OS's may not have 160# tcpdump or snoop available so this will not work (instead you may be 161# able to get Ethereal, run it, save to a file, then use normal mode). 162# There is a master index.html and the report index.html in a sub dir, 163# which is of the format out_YYYYMMDD-hhmm, eg "out_20031003-2221". 164# * Standalone, many - eg "chaosreader -S 5,12", this is where chaosreader 165# runs tcpdump/snoop and generates many log files, in this case it 166# samples 12 times for 5 minutes each. While this is running, the master 167# index.html can be viewed to watch progress, which links to minor 168# index.html reports in each sub directory. 169# * Standalone, redo - eg "chaosreader -ve -z", (the -z), this is where 170# a standalone capture was previously performed - and now you would like 171# to reprocess the logs - perhaps with different options (in this case, 172# "-ve"). It reads index.file to determine which capture logs to read. 173# * Standalone, endless - eg "chaosreader -S 5", like standalone many - 174# but runs forever (if you ever had the need?). Watch your disk space! 175# 176# Note: this is a work in progress, some of the code is a little unpolished. 177# 178# Advice: 179# * Run chaosreader in an empty directory. 180# * Create small packet dumps. Chaosreader uses around 5x the dump size 181# in memory. A 100Mb file could need 500Mb of RAM to process. 182# * Your tcpdump may allow "-s0" (entire packet) instead of "-s9000". 183# * Beware of using too much disk space, especially standalone mode. 184# * If you capture too many small connections giving a huge index.html, 185# try using the -m option to ignore small connections. eg "-m 1k". 186# * snoop logs may actually work better. Snoop logs are based on RFC1761, 187# however there are many varients of tcpdump/libpcap and this program 188# cannot read them all. If you have Ethereal you can create snoop logs 189# during the "save as" option. On Solaris use "snoop -o logfile". 190# * tcpdump logs may not be portable between OSs that use different sized 191# timestamps or endian. 192# * Logs are best created in a memory filesystem for speed, usually /tmp. 193# * For X11 or VNC playbacks, first practise by replaying a recent captured 194# session of your own. The biggest problem is colour depth, your screen 195# must match the capture. For X11 check authentication (xhost +), for 196# VNC check the viewers options (-8bit, "Hextile", ...) 197# * SSH analysis can be performed with the "sshkeydata" program as 198# demonstrated on http://www.brendangregg.com/sshanalysis.html . 199# chaosreader provides the input files (*.keydata) that sshkeydata 200# analyses. 201# 202# Bugs: The following assumptions may cause problems (check for new vers); 203# * A lower port number = the service type. Eg with ports 31247 and 23, 204# the actual type of session is telnet (23). This may not work for 205# some things (eg, VNC). 206# * Time based order is more important for 2-way sessions (eg telnet), 207# SEQ order is more import for 1-way transfers (eg ftp-data). 208# * One particular TCP session isn't active for long enough that the SEQ 209# number loops (or even wraps). 210# 211# WARNING: Please don't use this software for anything illegal. That definition 212# differs for every country, please check the law first. 213# This is a great network troubleshooting and development tool, not a 214# "cracking" or "hacking" tool - a misidentification that could render owning 215# this software illegal in some countries. 216# 217# SEE ALSO: wireshark (GUI packet viewer), dsniff (sniffing toolkit) 218# 219# COPYRIGHT: Copyright (c) 2003, 2004 Brendan Gregg. 220# Copyright (c) 2008 Indian Larry. 221# Copyright (c) 2011, 2012, 2013 Jens Lechtenbörger. 222# Copyright (c) 2014 Pavel Hančar, Jens Lechtenbörger, Pex pexnet0@gmail.com. 223# 224# This program is free software: you can redistribute it and/or modify 225# it under the terms of the GNU General Public License as published by 226# the Free Software Foundation, either version 3 of the License, or 227# (at your option) any later version. 228# 229# This program is distributed in the hope that it will be useful, 230# but WITHOUT ANY WARRANTY; without even the implied warranty of 231# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 232# GNU General Public License for more details. 233# 234# You should have received a copy of the GNU General Public License 235# along with this program. If not, see <http://www.gnu.org/licenses/>. 236# 237# Authors: Brendan Gregg [Sydney, Australia] 238# Indian Larry [http://refrequelate.blogspot.com/] 239# Jens Lechtenbörger [Münster, Germany] 240# Pavel Hančar [Brno, Czech Republic] 241# Pex [https://github.com/pexnet] 242# 243# Todo: 244# * Rework code to improve structure. 245# * Add more application protocol filters. ARP, RARP. 246# * Ensure current application filters are robust (more testing). 247# * Process captured filenames from FTP, HTTP and NFS transfers. 248# * Add more file types (magic numbers/frequency analysis). 249# * Process more IPv6 extension headers, ICMP types. 250# 251# 28-Sep-2003 Brendan Gregg Began writing this. 252# 08-Oct-2003 " " Released version 0.7 beta 253# 09-Oct-2003 " " Added telnet replays 254# 12-Oct-2003 " " Added IRC ports and replays 255# 19-Oct-2003 " " Made code more robust on different OSs 256# 01-Nov-2003 " " Code cleanup, complex data types, IPv6, ICMP 257# 03-Nov-2003 " " Added Standalone mode, standalone redo, ... 258# 05-Nov-2003 " " Added Image indexes, GETPOST indexes 259# 15-Nov-2003 " " Added HTTP proxy style log, hex dumps 260# 27-Jan-2004 " " Released experimental X11 & VNC processing 261# 30-Mar-2004 " " 802.11b, sorts, less RAM used, tun packets. 262# 01-May-2004 " " CLI enhanced, faster, SSH analysis. 263# 264# 11-Sep-2011, Jens Lechtenbörger: 265# - Switch from GPLv2 to GPLv3 266# - Integrate diff from 267# http://refrequelate.blogspot.com/2008/07/more-de-chunking-chaosreader-patch.html 268# to reassemble chunked HTTP transfers. 269# - Parse linux cooked captures, which result from listening on "any" 270# interface. (Chaosreader0.94 does not produce any output for such 271# pcap files.) 272# - Use HTTP content-type to identify file types such as HTML, XML, 273# Javascript, CSS; use those types for better file extensions than 274# "data". 275# - Uncompress gzip'ed data. 276# - Add new command line switch to show host names in HTTPlog and to 277# create href-links from HTTPlog rows to the corresponding rows in 278# the table on index.html. 279# - Several minor improvements (see comments with "JL:"). 280# 281# 24-Sep-2011, Jens Lechtenbörger: 282# - More systematic Content-Type handling based on MIME types. 283# - More image types included in Image Report based on MIME types. 284# 285# 4-Jan-2012, Jens Lechtenbörger: 286# - Parsing of DNS replies to show names instead of IP addresses (new 287# command line switch -d) and to save DNS replies as text files. 288# 289# 10-Feb-2012, Jens Lechtenbörger: 290# - Use file magic (again) to detect MIME type if HTTP's Content-Type is 291# application/octet-stream. (Some Web servers report images incorrectly.) 292# 293# 15-Mar-2013, Jens Lechtenbörger: 294# - Create additional HTTP log file in text format. That file contains one 295# line per GET request, which shows the referrer (if present) and indicates 296# whether cookies have been sent in the request or received in the reply. 297# 298# 15-Apr-2013, Jens Lechtenbörger: 299# - Link additional HTTP log file from index.html. 300# - Also look for images in plain/text Content-Types (seen in the wild). 301# - Extend GET/POST report to include all GETs; not only those including a 302# question mark (with parameters). 303# 304# 18-Apr-2013, Jens Lechtenbörger: 305# - Build new "External Image Report" (linked from index.html), where images 306# are embedded from their origin servers. 307# In contrast, the "Image Report" points to images on the local hard disk. 308# The new report may be more suitable for publication on Web pages as it 309# does not require to publish (potentially copyright protected) images. 310# - Parse CNAME DNS replies to show original host names (which are hopefully 311# more familiar than aliases). 312# - Show also empty parts on index.html that result from cache hits. 313# - Create directory passed after switch "-D". 314# 315# 12-Apr-2014, Pavel Hančar: 316# - Optimized hexadecimal dumps to use less memory. 317# - Modified "IP Count" to "IP and MAC Count". 318# - Fixed a few bugs concerning output. 319# 320# 14-Apr-2014, Jens Lechtenbörger: 321# - Also create HTML files for ports 8118 (polipo) and 9050 (Tor) and treat 322# both as HTTP traffic (quick hack, works for me). 323# - Improved handling of TCP streams with same source and destination IP 324# address (e.g., from localhost to localhost). 325# 326# 12 Jun 2014 Pex 327# - support for deflate 328# Jens frequently calls this program with options "-vden -D <somedir>". 329 330use Getopt::Long; 331use Benchmark; 332use IO::Uncompress::Gunzip qw(gunzip $GunzipError); 333use IO::Uncompress::Inflate qw(inflate $InflateError) ; 334use IO::Uncompress::RawInflate qw(rawinflate $RawInflateError) ; 335use Net::DNS::Packet; 336 337 338##################### 339# --- Variables --- 340# 341 342# 343# Some defaults 344# 345$PERL = "/usr/bin/perl"; # perl path for replay scripts 346$integerSize = length(pack('I',0)); # can make a difference for tcpdumps 347$the_date = scalar localtime(); # this is printed in the reports 348$WRAP = 108; # wordwrap chars 349$BM = 0; # benchmark counter 350$| = 1; # flush output 351 352# 353# The following is needed for old perl5 multiline matching. New perl5 uses 354# a "/s" on the RE (which is used in this program as well). 355# 356#$* = 1; # old perl5 357 358# 359# These ports have been selected to be saved as coloured 2-way HTML files 360# 361@Save_As_HTML_TCP_Ports = (21,23,25,79,80,109,110,119,143,513,514,1080, 362 3128,4110,5000,5555,6660,6665,6666,6667,6668,7000,8000,8080,8118,9000,9050); 363@Save_As_HTML_UDP_Ports = (53); 364 365# 366# These ports have been selected to be saved as realtime playback scripts 367# (telnet, login, and numerous IRC ports) 368# 369@Save_As_TCP_Playback_Ports = (23,513,4110,5000,5555,6660,6666,6667, 370 6668,7000,8000,9000); 371@Save_As_UDP_Playback_Ports = (7); 372 373# 374# These are the X11 ports to save as X11 playback scripts 375# 376@Save_As_X11_Playback_Ports = (6000,6001,6002,6003,6004,6005,6006,6007); 377 378# 379# These X11 ports will have the text saved as coloured 2-way HTML files 380# 381@Save_As_HTML_X11_Ports = (6000,6001,6002,6003,6004,6005,6006,6007); 382 383# 384# These are the VNC ports to save as VNC playback scripts 385# 386@Save_As_VNC_Playback_Ports = (5900,5901,5902,5903,5904,5905,5906,5907); 387 388# 389# --- Arguments --- 390# 391&Process_Command_Line_Arguments(); 392 393### Record program start 394$Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 395$Bench{$BM}{text} = "Program Start"; 396 397# 398# Load some lookup tables for number -> name translations. 399# 400&Load_Etc_Services(); 401&Set_IP_Protocols(); 402&Set_ICMP_Types(); 403&Set_Result_Names(); 404&Set_X11_Codes(); 405&Set_X11_KeyCodes(); 406&Set_VNC_Codes(); 407&Set_MIME_Types(); #JL 408&Set_DNS(); #JL 409 410########################### 411# --- MODE 1 - Normal --- # 412########################### 413 414# 415# Process log files, 416# 417if ($Arg{normal}) { 418 # 419 # Initial values 420 # 421 $frame = 0; $number = 0; 422 %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); 423 424 ### Print version 425 &Print_Welcome(); 426 427 ###################################### 428 # --- INPUT - Read Packet Log(s) --- 429 # 430 431 foreach $filename (@{$Arg{infiles}}) { 432 # 433 # Check input file type and Open 434 # 435 &Open_Input_File($filename); 436 437 # 438 # Read though the entire input file, saving all packet 439 # data in memory (mainly %TCP and %UDP). 440 # 441 &Read_Input_File(); 442 } 443 444 445 ############################################# 446 # --- OUTPUT - Process TCP/UDP Sessions --- 447 # 448 449 ### cd to output 450 &Chdir($Arg{output_dir}); 451 &Print_Header2(); 452 453 ### Determine Session and Stream time order 454 %Index = (); %Image = (); %ExtImage = (); %GETPOST = (); 455 &Sort_Index(); 456 457 # 458 # Process %TCP and create session* output files, write %Index 459 # 460 &Process_TCP_Sessions(); 461 462 # 463 # Process %UDP and create session* output files, write %Index 464 # 465 &Process_UDP_Streams(); 466 467 # 468 # Process %ICMP 469 # 470 &Process_ICMP(); 471 472 # 473 # Create Index Files from %Index 474 # 475 &Create_Index_Files(); 476 &Create_Log_Files(); 477 478 ############### 479 # --- END --- 480 # 481 &Print_Footer1(); 482} 483 484 485############################### 486# --- MODE 2 - Standalone --- # 487############################### 488 489elsif ($Arg{standalone}) { 490 491 ############################################################ 492 # --- STANDALONE - Create Packet Logs and Process them --- 493 # 494 495 $limit = $Arg{count}; 496 $filenum = 0; 497 498 ### Check for the sniffer command 499 &Check_Command(); 500 501 ### cd to output 502 &Chdir($Arg{output_dir}); 503 504 ### Print welcome 505 &Print_Welcome(); 506 507 # 508 # MAIN LOOP 509 # 510 while ($limit != 0) { 511 # 512 # Create a meaningful directory and filename 513 # 514 @Times = localtime(); 515 $dirname = sprintf("out_%d%02d%02d-%02d%02d",($Times[5]+1900), 516 $Times[4],$Times[3],$Times[2],$Times[1]); 517 $filename = "$dirname.log"; 518 519 # 520 # Initial values 521 # 522 $frame = 0; $number = 0; 523 %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); 524 525 # 526 # Record details in a Master Index 527 # 528 $Master[$filenum]{starttime} = scalar localtime(); 529 $Master[$filenum]{duration} = - time(); # will +end time 530 $Master[$filenum]{dir} = $dirname; 531 $Master[$filenum]{file} = $filename; 532 533 # 534 # Create and cd to output dir 535 # 536 mkdir ("$dirname",0755) || die "ERROR01: Couldn't mkdir (perms?): $!\n"; 537 chdir "$dirname" || die "ERROR02: Couldn't cd $dirname: $!\n"; 538 539 print "\nCreating log: $dirname/$filename\n" unless $Arg{quiet}; 540 541 # 542 # fork, so that one process can exec tcpdump/snoop while the other 543 # sleeps and then kills it. 544 # 545 $pid = fork(); 546 die "ERROR03: Can't fork (resources?): $!\n" if (! defined $pid); 547 548 if ($pid == 0) { 549 ############################### 550 # --- CREATE - Packet Log --- 551 # 552 553 print "Running: $command $filename $Arg{filter}\n" 554 unless $Arg{quiet}; 555 ### exec, so $pid points to sniffer 556 exec("$command $filename $Arg{filter}") && 557 die "ERROR04: couldn't run $command file: $!\n"; 558 } else { 559 ### Wait for logfile to be populated 560 sleep($Arg{mins} * 60); 561 562 ### Kill child (TERM, INT) 563 kill 15, $pid; 564 kill 2, $pid; 565 } 566 exit if $pid == 0; # check for impossibility 567 568 569 ### Record end time, duration, size 570 $Master[$filenum]{endtime} = scalar localtime(); 571 $Master[$filenum]{duration} += time(); 572 # finish writing the log before reading it's size 573 system("sync") if (($^O eq "linux") || ($^O eq "solaris")); 574 $Master[$filenum]{size} = -s "$filename"; 575 576 print "\nProcessing: $dirname/$filename\n" unless $Arg{quiet}; 577 $bak = $Arg{quiet}; $Arg{quiet} = 1; 578 579 ############################### 580 # --- INPUT - Process Log --- 581 # 582 &Open_Input_File($filename); 583 584 ### Populate memory (%TCP, %UDP, ...). 585 &Read_Input_File(); 586 587 ############################################# 588 # --- OUTPUT - Process TCP/UDP Sessions --- 589 # 590 591 ### Determine Session and Stream time order 592 %Index = (); %Image = (); %ExtImage = (); %GETPOST = (); 593 &Sort_Index(); 594 595 ### Process %TCP, %UDP, ..., create output fies, write %Index 596 &Process_TCP_Sessions(); 597 &Process_UDP_Streams(); 598 &Process_ICMP(); 599 600 ### Create Index Files from %Index 601 &Create_Index_Files(); 602 &Create_Log_Files(); 603 604 605 chdir ".." || die "ERROR05: Couldn't cd ..: $!\n"; 606 607 $Arg{quiet} = $bak; 608 609 ### Create Master Index from @Master 610 &Create_Index_Master(); 611 612 $limit--; 613 $filenum++; 614 } 615 616} 617 618 619########################## 620# --- MODE 3 - Redo --- # 621########################## 622 623elsif ($Arg{redo}) { 624 625 ############################################################# 626 # --- STANDALONE REDO - Redo last run from sniffer logs --- 627 # 628 629 $filenum = 0; 630 631 ### Read index.file for logs to process 632 &Load_Index_File(); 633 634 ### Print welcome 635 &Print_Welcome(); 636 637 # 638 # MAIN LOOP 639 # 640 for ($index=0; $index <= $#Master; $index++) { 641 642 ### Get previous run values 643 $dirname = $Master[$index]{dir}; 644 $filename = $Master[$index]{file}; 645 646 ### Initial values 647 $frame = 0; $number = 0; 648 %IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = (); 649 650 ### Create and cd to output dir 651 chdir "$dirname" || die "ERROR06: Couldn't cd $dirname: $!\n"; 652 653 print "Processing: $dirname/$filename\n" unless $Arg{quiet}; 654 $bak = $Arg{quiet}; $Arg{quiet} = 1; 655 656 ############################### 657 # --- INPUT - Process Log --- 658 # 659 &Open_Input_File($filename); 660 661 ### Populate memory (%TCP, %UDP, ...). 662 &Read_Input_File(); 663 664 ############################################# 665 # --- OUTPUT - Process TCP/UDP Sessions --- 666 # 667 668 ### Determine Session and Stream time order 669 %Index = (); %Image = (); %ExtImage = (); %GETPOST = (); 670 &Sort_Index(); 671 672 ### Process %TCP, %UDP, ..., create output fies, write %Index 673 &Process_TCP_Sessions(); 674 &Process_UDP_Streams(); 675 &Process_ICMP(); 676 677 ### Create Index Files from %Index 678 &Create_Index_Files(); 679 &Create_Log_Files(); 680 681 chdir ".." || die "ERROR07: Couldn't cd ..: $!\n"; 682 $Arg{quiet} = $bak; 683 684 $limit--; 685 $filenum++; 686 } 687 ### Create Master Index from @Master 688 &Create_Index_Master(); 689} 690 691 692# 693# BENCHMARK REPORT 694# 695if ($Arg{bench}) { 696 $Bench{++$BM}{mark} = new Benchmark; 697 $Bench{$BM}{text} = "Program End"; 698 699 print "\nBenchmarks,\n\n"; 700 for ($bm=1; $bm <= $BM; $bm++) { 701 $bdiff = timediff($Bench{$bm}{mark},$Bench{1}{mark}); 702 printf(" %-32s %s\n",$Bench{$bm}{text},timestr($bdiff)); 703 } 704} 705 706 707##################### 708# --- SUBROUTINES --- 709 710# (Most of these subroutines are used as shortcuts to code, not traditional 711# scoped subroutines as with other languages) 712 713 714 715# Open_Input_File - open the packet log specified. This checks the header 716# of the file to determine whether it is a tcpdump/libpcap or snoop 717# log (including several styles of tcpdump/libpcap). 718# 719sub Open_Input_File { 720 721 my $infile = shift; 722 my ($length,$size); 723 724 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 725 $Bench{$BM}{text} = "Open Input File"; 726 727 print "Opening, $infile\n\n" unless $Arg{quiet}; 728 729 # 730 # Open packet log 731 # 732 open(INFILE,$infile) || die "Can't open $infile: $!\n"; 733 binmode(INFILE); # for backward OSs 734 735 # 736 # Fetch header 737 # 738 $length = read(INFILE,$header,8); 739 die "ERROR08: Can't read from $infile\n" if $length < 8; 740 741 ### Print status 742 print "Reading file contents,\n" unless $Arg{quiet}; 743 $SIZE = -s $infile; 744 745 # 746 # Try to determine if this is a tcpdump or a snoop file 747 # 748 ($ident) = unpack('a8',$header); 749 750 if ($ident =~ /^snoop/) { 751 752 $TYPE = "snoop"; 753 $length = read(INFILE,$header,8); 754 ($version,$type) = unpack('NN',$header); 755 756 } elsif ($ident =~ /^\241\262\303\324|^\324\303\262\241/ || 757 $ident =~ /^\241\262\315\064|^\064\315\262\241/) { 758 759 $TYPE = "tcpdump"; 760 $ident = unpack('a4',$header); # try again 761 # standard/modified defines style, 1/2 defines endian 762 if ($ident =~ /^\241\262\303\324/) { $STYLE = "standard1"; } 763 if ($ident =~ /^\324\303\262\241/) { $STYLE = "standard2"; } 764 if ($ident =~ /^\241\262\315\064/) { $STYLE = "modified1"; } 765 if ($ident =~ /^\064\315\262\241/) { $STYLE = "modified2"; } 766 if ($STYLE =~ /1$/) { 767 # reread in big-endian 768 ($ident,$major,$minor) = unpack('a4nn',$header); 769 } else { 770 # reread in little-endian 771 ($ident,$major,$minor) = unpack('a4vv',$header); 772 } 773 774 # 775 # Check tcpdump header carefully to ensure this is ver 2.4. 776 # 777 if ($major != 2 && $minor != 4) { 778 # 779 # Die if this is an unknown version. (there could 780 # be new vers of tcpdump/libpcap in the future). 781 # 782 print STDERR "ERROR09: Wrong tcpdump version "; 783 print STDERR "($version.$type).\n(expected 2.4).\n"; 784 exit 1; 785 } 786 # 787 # Nudge the filehandle past the rest of the header... 788 # 789 $length = read(INFILE,$header_rest,16); 790 791 } else { 792 # 793 # Die - unknown file format 794 # 795 print STDERR "ERROR10: Input dosen't look like a tcpdump or "; 796 print STDERR "snoop output file.\n\tIf it is tcpdump, it "; 797 print STDERR "may be a wrong or new version.\n"; 798 exit 1; 799 } 800 801 ### Record the filename into the global %Arg 802 $Arg{infile} = $infile; 803} 804 805 806 807# Read_Input_File - this subroutine loops through the records in the packet 808# log, storing all the TCP and UDP data into %TCP and %UDP. (see the end 809# of the program for the structure of these data types). %Count is also 810# populated with various frequency counts. 811# 812sub Read_Input_File { 813 my ($trailers,$pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length, 814 $ppp_protocol,$wless_fc,$wless_version,$wless_type,$wless_duration, 815 $wless_subtype,$wless_from,$wless_to,$wless_flag,$wless_WEP, 816 $wless_bss,$wless_src,$wless_dest,$wless_cksum,$llc_head,$llc_control, 817 $llc_org,$llc_type,$wless_OK,$bytes,$counter,$packets); 818 819 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 820 $Bench{$BM}{text} = "Read Input File - start"; 821 822 local $packet = 0; # counter 823 if ($TYPE eq "snoop") { 824 $bytes = 16; 825 } else { 826 $bytes = 24; 827 } 828 829 # 830 # --- Pass #1, Store IP data in memory (%IP) -- 831 # 832 while (1) { 833 # 834 # --- Read Record from Log --- 835 # 836 if ($TYPE eq "snoop") { 837 &Read_Snoop_Record(); # will "last" on error 838 $packet_data = $snoop_data; 839 $packet_time = $snoop_seconds; 840 $packet_timefull = $snoop_seconds + $snoop_msecs/1000000; 841 $record_size = $snoop_length_rec; 842 } else { 843 &Read_Tcpdump_Record(); # will "last" on error 844 $packet_data = $tcpdump_data; 845 $packet_time = $tcpdump_seconds; 846 $packet_timefull = $tcpdump_seconds + $tcpdump_msecs/1000000; 847 $record_size = $tcpdump_length + ($integerSize * 2 + 8); 848 } 849 850 ### Print status summary 851 unless ($Arg{quiet}) { 852 $bytes += $record_size; 853 if (($packet % 16) == 0) { 854 printf("%s %2.0f%% (%d/%d)","\b"x24, 855 (100*$bytes/$SIZE),$bytes,$SIZE); 856 } 857 } 858 859 # 860 # --- Parse TCP/IP layers (a little ;) --- 861 # 862 863 #------------------------------------------------------------------- 864 # 865 # Wireless, 802.11b 866 # 867 868 $decoded = 0; # this flag is true if wireless was found 869 870 # unpack a little first, (efficiency) 871 ($wless_fc) = unpack('H4',$packet_data); 872 873 # this matches on possible send or receive wireless traffic, however 874 # this could also be the start of an 802.3 frame - making this part 875 # of a MAC address. (The IEEE list on OUIs had these as unassigned). 876 if ($wless_fc =~ /^080[1256]/) { 877 # now dig deeper, 878 # (this is one form of 802.11 - the form we are interested 879 # in, however note that there is a lot more to 802.11). 880 ($wless_fc,$wless_duration,$wless_bss,$wless_src, 881 $wless_dest,$wless_cksum,$llc_head,$llc_control,$llc_org, 882 $llc_type,$ether_data) 883 = unpack('nnH12H12H12na2CH6H4a*',$packet_data); 884 885 $wless_to = $wless_fc & 1; 886 887 # Check this is IP and encapsulated Ethernet, 888 if (($llc_type eq "0800") && ($llc_org eq "000000")) { 889 890 ### Populate ether variables for use later on 891 $ether_type = $llc_type; 892 if ($wless_to) { 893 $ether_dest = $wless_dest; 894 $ether_src = $wless_src; 895 } else { 896 $ether_dest = $wless_src; 897 $ether_src = $wless_dest; 898 } 899 900 $decoded = 1; # remember we did this 901 } 902 # (else try redecoding this using 802.3) 903 } 904 905 #------------------------------------------------------------------- 906 # 907 # Tun device 908 # 909 910 # unpack a little first, (efficiency) 911 ($tun_id) = unpack('H8',$packet_data); 912 913 # this checks if the frame looks like a tun device frame 914 if ($tun_id eq "02000000") { 915 # now dig deeper, 916 ($tun_id,$ether_data) = unpack('a4a*',$packet_data); 917 $ether_src = "0"; 918 $ether_dest = "0"; 919 $ether_type = "0800"; 920 921 $decoded = 1; # remember we did this 922 } 923 924 #------------------------------------------------------------------- 925 # 926 # Ethernet, 802.3 927 # 928 929 ### Unpack ether data 930 ($ether_dest,$ether_src,$ether_type,$ether_data) = 931 unpack('H12H12H4a*',$packet_data) unless $decoded; 932 933 ### Count ether types seen 934 $Count{EtherType}{$ether_type}++; 935 $CountMaster{EtherType}{$ether_type}++; 936 937 # 938 # Process extended Ethernet types (wireless, PPPoE, VLAN) 939 # 940 941 ### PPPoE 942 if ($ether_type eq "8864") { 943 ($pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length, 944 $ppp_protocol,$ether_data) = unpack("CCnnna*",$ether_data); 945 946 ### Skip anything but data (we just want data - code 0) 947 next if $pppoe_code != 0; 948 949 # (May like to add code here later to process $ppp_protocol, 950 # eg, to process LCP). 951 } 952 ### VLAN tagged 953 elsif ($ether_type eq "8100") { 954 ($vlan_PCP, $orig_ether_type, $ip_rest) = unpack('H4H4a*', $ether_data); 955 $ether_data = $ip_rest; 956 } 957 958 elsif (($ether_type ne "0800") && ($ether_type ne "86dd")) { 959 # JL: Try linux cooked capture 960 ($lptype,$lladdr_type,$lladdr_len, 961 $ether_src,$ll_dummy,$ether_type,$ether_data) = 962 unpack('nnnH12nH4a*',$packet_data) unless $decoded; 963 if ($ether_type ne "0800") { 964 next; 965 } 966 } 967 968 #------------------------------------------------------------------- 969 # 970 # IP 971 # 972 973 ### Check for IP ver 974 ($ip_verNihl,$ip_rest) = unpack('Ca*',$ether_data); 975 $ip_ver = $ip_verNihl & 240; 976 $ip_ver = $ip_ver >> 4; 977 978 if ($ip_ver == 4) { 979 980 #----------------------------------------------------------- 981 # 982 # IPv4 983 # 984 985 ### Unpack IP data 986 ($ip_verNihl,$ip_tos,$ip_length,$ip_ident,$ip_flagNfrag, 987 $ip_ttl,$ip_protocol,$ip_checksum,@ip_src[0..3], 988 @ip_dest[0..3],$ip_data) = unpack('CCnnnCCa2CCCCCCCCa*', 989 $ether_data); 990 991 ### Get frag and flag data 992 $ip_frag = $ip_flagNfrag & 8191; 993 $ip_flag = $ip_flagNfrag & 57344; 994 $ip_flag = $ip_flag >> 13; 995 $ip_MF = $ip_flag & 1; 996 997 ### Strip off IP options if present 998 $ip_ihl = $ip_verNihl & 15; 999 $ip_ihl = $ip_ihl << 2; 1000 $ip_options_num = $ip_ihl - 20; 1001 if ($ip_options_num > 0) { 1002 ($ip_options,$ip_data) = 1003 unpack("a${ip_options_num}a*",$ip_data); 1004 } 1005 1006 ### Strip off Ethernet trailers 1007 $ip_dlength = $ip_length - $ip_options_num - 20; 1008 ($ip_data,$trailers) = unpack("a${ip_dlength}a*",$ip_data); 1009 1010 ### Build text strings of IP addresses 1011 $ip_src = sprintf("%u.%u.%u.%u",@ip_src); 1012 $ip_dest = sprintf("%u.%u.%u.%u",@ip_dest); 1013 1014 } elsif ($ip_ver == 6) { 1015 1016 #----------------------------------------------------------- 1017 # 1018 # IPv6 1019 # 1020 ($ip_verNihl,$ip_flow,$ip_length,$ip_next,$ip_hop, 1021 @ip_src[0..15],@ip_dest[0..15],$ip_data) = 1022 unpack('Ca3nCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCa*', 1023 $ether_data); 1024 $ip_protocol = $ip_next; 1025 1026 ### Build text strings of IP addresses 1027 $ip_src = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x", 1028 @ip_src); 1029 $ip_dest = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x", 1030 @ip_dest); 1031 1032 ### Compress IPv6 text Address 1033 $ip_src =~ s/:00:/:0:/g; 1034 $ip_src =~ s/:00:/:0:/g; 1035 $ip_dest =~ s/:00:/:0:/g; 1036 $ip_dest =~ s/:00:/:0:/g; 1037 $ip_src =~ s/(:0)+/::/; 1038 $ip_dest =~ s/(:0)+/::/; 1039 1040 1041 # 1042 # Check for IPv6 Fragmentation (embedded) 1043 # 1044 if ($ip_protocol == 44) { 1045 ($ip_next,$ip_reserved,$ip_fragNmf,$ip_ident,$ip_data) 1046 = unpack('CCnNa*',$ip_data); 1047 $ip_protocol = $ip_next; 1048 $ip_MF = $ip_fragNmf & 1; 1049 $ip_frag = $ip_fragNmf >> 3; 1050 } else { 1051 $ip_MF = 0; 1052 $ip_ident = 0; 1053 $ip_frag = 0; 1054 } 1055 1056 } else { 1057 ### Not IPv4 or IPv6 - could be LCP (skip for now) 1058 next; 1059 } 1060 1061 ### Count IP Protocols seen 1062 $Count{IPprotocol}{$ip_protocol}++; 1063 $CountMaster{IPprotocol}{$ip_protocol}++; 1064 1065 ### Count IP Addresses seen 1066 $Count{IP}{"$ip_src,$ether_src"}++; 1067 $CountMaster{IP}{$ip_src}++; 1068 1069 ### Generate unique IP id (not just the ident) 1070 $ip_id = &Generate_IP_ID($ip_src,$ip_dest,$ip_ident); 1071 1072 # 1073 # Store IP data in %IP so we can do frag reassembly next 1074 # 1075 if (! defined $IP{id}{$ip_id}{StartTime}) { 1076 $IP{time}{$packet_timefull}{ver} = $ip_ver; 1077 $IP{time}{$packet_timefull}{src} = $ip_src; 1078 $IP{time}{$packet_timefull}{dest} = $ip_dest; 1079 $IP{time}{$packet_timefull}{protocol} = $ip_protocol; 1080 $IP{time}{$packet_timefull}{frag}{$ip_frag} = $ip_data; 1081 if ($snoop_drops || $tcpdump_drops) { 1082 $IP{time}{$packet_timefull}{drops} = 1; 1083 } 1084 # 1085 # If there are more fragments, remember this starttime 1086 # 1087 unless (($ip_MF == 0) && ($ip_frag == 0)) { 1088 $IP{id}{$ip_id}{StartTime} = $packet_timefull; 1089 } 1090 if (($ip_MF == 1) || ($ip_frag > 0)) { 1091 $IP{time}{$packet_timefull}{fragged} = 1; 1092 } 1093 } else { 1094 $start_time = $IP{id}{$ip_id}{StartTime}; 1095 $IP{time}{$start_time}{frag}{$ip_frag} = $ip_data; 1096 if ($snoop_drops || $tcpdump_drops) { 1097 $IP{time}{$packet_timefull}{drops} = 1; 1098 } 1099 if ($ip_MF == 0) { 1100 # 1101 # Comlpete this IP packet. This assumes that the 1102 # last frag arrives last. 1103 # 1104 undef $IP{ident}{StartTime}{$ip_id}; 1105 } 1106 } 1107 $packet++; 1108 } 1109 1110 close INFILE; 1111 1112 ### Print status summary 1113 unless ($Arg{quiet}) { 1114 printf("%s %2.0f%% (%d/%d)","\b"x24, 1115 100,$bytes,$SIZE); 1116 print "\nReassembling packets,\n"; 1117 } 1118 1119 1120 1121 ################################################################### 1122 # --- Pass #2, Reassemble IP data in %IP; create %TCP and %UDP --- 1123 # 1124 1125 &Print_Header1() if $Arg{debug}; 1126 $packets = $packet; 1127 $packet = 0; 1128 @Times = sort { $a <=> $b } ( keys(%{$IP{time}}) ); 1129 foreach $time (@Times) { 1130 1131 ### Print status summary 1132 unless ($Arg{quiet}) { 1133 if (($packet % 16) == 0) { 1134 printf("%s %2.0f%% (%d/%d)","\b"x32, 1135 (100*$packet/$packets),$packet,$packets); 1136 } 1137 } 1138 1139 # 1140 # Get IP data from %IP 1141 # 1142 $ip_ver = $IP{time}{$time}{ver}; 1143 $ip_src = $IP{time}{$time}{src}; 1144 $ip_dest = $IP{time}{$time}{dest}; 1145 $ip_protocol = $IP{time}{$time}{protocol}; 1146 $drops = $IP{time}{$time}{drops}; 1147 undef $ip_data; 1148 1149 # 1150 # Reassemble IP frags 1151 # 1152 if (defined $IP{time}{$time}{fragged}) { 1153 @IP_Frags = sort {$a <=> $b} (keys(%{$IP{time}{$time}{frag}})); 1154 1155 ### If never recieved the start of the packet, skip 1156 if ($IP_Frags[0] != 0) { next; } 1157 1158 foreach $ip_frag (@IP_Frags) { 1159 $ip_data .= $IP{time}{$time}{frag}{$ip_frag}; 1160 } 1161 } else { 1162 $ip_data = $IP{time}{$time}{frag}{0}; 1163 } 1164 $length = length($ip_data); 1165 1166 # 1167 # --- UDP --- 1168 # 1169 if ($ip_protocol == 17 && $Arg{output_UDP}) { 1170 &Process_UDP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops); 1171 } 1172 1173 # 1174 # --- TCP --- 1175 # 1176 if ($ip_protocol == 6 && $Arg{output_TCP}) { 1177 &Process_TCP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops); 1178 } 1179 1180 # 1181 # --- ICMP --- 1182 # 1183 if ($ip_protocol == 1 && $Arg{output_ICMP}) { 1184 &Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops, 1185 "ICMP"); 1186 } 1187 1188 # 1189 # --- ICMPv6 --- 1190 # 1191 if ($ip_protocol == 58 && $Arg{output_ICMP}) { 1192 &Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops, 1193 "ICMPv6"); 1194 } 1195 1196 # 1197 # Skip packet if it isn't TCP (protocol = 6). (Will add routines for 1198 # ICMP, ARP, RARP later on)... 1199 # 1200 1201 $packet++; 1202 1203 ### Memory Cleanup 1204 delete $IP{time}{$time}; 1205 1206 } 1207 1208 ### Memory Cleanup 1209 undef %IP; 1210 1211 ### Print status summary 1212 unless ($Arg{quiet}) { 1213 printf("%s %2.0f%% (%d/%d)\n","\b"x24, 1214 100,$packet,$packets); 1215 } 1216 1217 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 1218 $Bench{$BM}{text} = "Read Input File - end"; 1219} 1220 1221 1222 1223# Process_TCP_Packet - process a TCP packet and store it in memory. It takes 1224# the raw ip data and populates the data structure %TCP. (and %Count). 1225# 1226sub Process_TCP_Packet { 1227 1228 my $ip_data = shift; 1229 my $ip_src = shift; 1230 my $ip_dest = shift; 1231 my $time = shift; 1232 my $drops = shift; 1233 my $copy; 1234 1235 #------------------------------------------------------------------- 1236 # 1237 # TCP 1238 # 1239 1240 ### Unpack TCP data 1241 ($tcp_src_port,$tcp_dest_port,$tcp_seq,$tcp_ack,$tcp_offset,$tcp_flags, 1242 $tcp_header_rest,$tcp_data) = unpack('nnNNCCa6a*',$ip_data); 1243 1244 ### Strip off TCP options, if present 1245 $tcp_offset = $tcp_offset >> 4; # chuck out reserved bits 1246 $tcp_offset = $tcp_offset << 2; # now times by 4 1247 $tcp_options_num = $tcp_offset - 20; 1248 if ($tcp_options_num > 0) { 1249 ($tcp_options,$tcp_data) = 1250 unpack("a${tcp_options_num}a*",$tcp_data); 1251 } 1252 1253 ### Fetch length and FIN,RST flags 1254 $tcp_length_data = length($tcp_data); 1255 $tcp_fin = $tcp_flags & 1; 1256 $tcp_syn = $tcp_flags & 2; 1257 $tcp_rst = $tcp_flags & 4; 1258 $tcp_ack = $tcp_flags & 16; 1259 1260 $copy = $tcp_data; 1261 1262 # 1263 # Generate $session_id as a unique id for this stream 1264 # (this is built from host:port,host:port - sorting on port). 1265 # 1266 ($session_id,$from_server) = &Generate_SessionID($ip_src,$tcp_src_port, 1267 $ip_dest,$tcp_dest_port,"TCP"); 1268 1269 ### Record direction if single SYN was seen 1270 if ($tcp_syn && ! $tcp_ack) { 1271 $TCP{id}{$session_id}{source} = $ip_src; 1272 $TCP{id}{$session_id}{source_port} = $tcp_src_port; 1273 # better repeat this, 1274 ($session_id,$from_server) = &Generate_SessionID($ip_src, 1275 $tcp_src_port,$ip_dest,$tcp_dest_port,"TCP"); 1276 } 1277 1278 ### Count TCP Ports seen 1279 if ($from_server) { 1280 $Count{TCPport}{$tcp_src_port}++; 1281 $CountMaster{TCPport}{$tcp_src_port}++; 1282 } else { 1283 $Count{TCPport}{$tcp_dest_port}++; 1284 $CountMaster{TCPport}{$tcp_dest_port}++; 1285 } 1286 1287 # 1288 # Flag this session as a Partial if either tcpdump or snoop 1289 # confesses to dropping packets. 1290 # 1291 $TCP{id}{$session_id}{Partial}++ if $drops; 1292 1293 ### Store size 1294 $TCP{id}{$session_id}{size} += length($tcp_data); 1295 1296 ### Store the packet timestamp for the first seen packet 1297 if (! defined $TCP{id}{$session_id}{StartTime}) { 1298 $TCP{id}{$session_id}{StartTime} = $time; 1299 1300 ### Store other info once 1301 if ($from_server) { 1302 $TCP{id}{$session_id}{src} = $ip_dest; 1303 $TCP{id}{$session_id}{dest} = $ip_src; 1304 $TCP{id}{$session_id}{src_port} = $tcp_dest_port; 1305 $TCP{id}{$session_id}{dest_port} = $tcp_src_port; 1306 } else { 1307 $TCP{id}{$session_id}{src} = $ip_src; 1308 $TCP{id}{$session_id}{dest} = $ip_dest; 1309 $TCP{id}{$session_id}{src_port} = $tcp_src_port; 1310 $TCP{id}{$session_id}{dest_port} = $tcp_dest_port; 1311 } 1312 } 1313 1314 ### Store the packet timestamp in case this is the last packet 1315 $TCP{id}{$session_id}{EndTime} = $time; 1316 1317 ### Print status line 1318 printf "%6s %-45s %s\n",$packet,$session_id,$length 1319 if $Arg{debug}; 1320 1321 1322 # 1323 # --- Store Session Data in Memory --- 1324 # 1325 # Since TCP is usually the bulk of the data, we minimise 1326 # the number of copies of data in memory. UDP and ICMP 1327 # are handled differently. 1328 1329 if ($from_server) { 1330 # 1331 # Populate %TCP{id}{}{time} with raw traffic by time. 1332 # This is the master structure to store the data. 1333 # 1334 $TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data; 1335 $TCP{id}{$session_id}{time}{$time}{dir} .= "A"; 1336 1337 # 1338 # 1339 # Populate %TCP{id}{}{Aseq} with server to client 1340 # 1-way raw traffic, with the TCP sequence number as 1341 # the key (for future reassembly). 1342 # 1343 # This is a pointer to the time structure above, 1344 # to save on memory used (originally stored a 1345 # duplicate copy of the data). 1346 # 1347 if ((! defined $TCP{id}{$session_id}{Aseq}{$tcp_seq}) || 1348 (length(${$TCP{id}{$session_id}{Aseq}{$tcp_seq}}) < 1349 length($tcp_data))) { 1350 $TCP{id}{$session_id}{Aseq}{$tcp_seq} = 1351 \$TCP{id}{$session_id}{time}{$time}{data}; 1352 } 1353 1354 } else { 1355 # 1356 # Populate %TCP{id}{}{Btime} with raw 1-way traffic by time. 1357 # This is the master structure to store the data. 1358 # 1359 $TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data; 1360 $TCP{id}{$session_id}{time}{$time}{dir} .= "B"; 1361 1362 # 1363 # 1364 # Populate %TCP{id}{}{Bseq} with client to server 1365 # 1-way raw traffic, with the TCP sequence number as 1366 # the key (for future reassembly). 1367 # 1368 # This is a pointer to the time structure above, 1369 # to save on memory used (originally stored a 1370 # duplicate copy of the data). 1371 # 1372 if ((! defined $TCP{id}{$session_id}{Bseq}{$tcp_seq}) || 1373 (length(${$TCP{id}{$session_id}{Bseq}{$tcp_seq}}) < 1374 length($tcp_data))) { 1375 $TCP{id}{$session_id}{Bseq}{$tcp_seq} = 1376 \$TCP{id}{$session_id}{time}{$time}{data}; 1377 } 1378 } 1379 # 1380 # Populate %Hex{TCP}{} with data necessary to generate coloured HTML 2-way 1381 # traffic, if needed. 1382 # 1383 if ($Arg{output_hex}) { 1384 push(@{$Hex{"TCP"}{$session_id}}, [$from_server, $tcp_data]); 1385 } 1386} 1387 1388 1389 1390# Process_UDP_Packet - process a UDP packet and store it in memory. It takes 1391# the raw ip data and populates the data structure %UDP. 1392# 1393sub Process_UDP_Packet { 1394 1395 my $ip_data = shift; 1396 my $ip_src = shift; 1397 my $ip_dest = shift; 1398 my $time = shift; 1399 my $drops = shift; 1400 my $copy; 1401 1402 #------------------------------------------------------------------- 1403 # 1404 # UDP 1405 # 1406 1407 ### Unpack UDP data 1408 ($udp_src_port,$udp_dest_port,$udp_length,$udp_checksum, 1409 $udp_data) = unpack('nnnna*',$ip_data); 1410 1411 # 1412 # Generate $session_id as a unique id for this stream 1413 # (this is built from host:port,host:port - sorting on port). 1414 # 1415 ($session_id,$from_server) = &Generate_SessionID($ip_src,$udp_src_port, 1416 $ip_dest,$udp_dest_port,"UDP"); 1417 1418 # 1419 # Flag this session as a Partial if either tcpdump or snoop 1420 # confesses to dropping packets. 1421 # 1422 $UDP{id}{$session_id}{Partial}++ if $drops; 1423 1424 ### Store size 1425 $UDP{id}{$session_id}{size} += length($udp_data); 1426 1427 ### Count UDP ports seen 1428 if ($from_server) { 1429 $Count{UDPport}{$udp_src_port}++; 1430 $CountMaster{UDPport}{$udp_src_port}++; 1431 } else { 1432 $Count{UDPport}{$udp_dest_port}++; 1433 $CountMaster{UDPport}{$udp_dest_port}++; 1434 } 1435 1436 # JL: DNS parsing 1437 if (($udp_src_port == 53) || ($udp_dest_port == 53)) { 1438 &Process_DNS($udp_data, $session_id); 1439 } 1440 1441 # 1442 # --- Store Stream Data in Memory --- 1443 # 1444 1445 if ($from_server) { 1446 # 1447 # Populate %UDP{id}{}{RawA} with server to client 1448 # 1-way raw traffic 1449 # 1450 $UDP{id}{$session_id}{RawA} .= $udp_data; 1451 1452 # 1453 # Populate %UDP{id}{}{BothHTML} with coloured HTML 1454 # 2-way traffic, blue for server to client 1455 # 1456 $copy = &Desex_HTML($udp_data); 1457 $UDP{id}{$session_id}{BothHTML} .= 1458 "<font color=\"blue\">$copy</font>"; 1459 } else { 1460 # 1461 # Populate %UDP{id}{}{RawB} with client to server 1462 # 1-way raw traffic 1463 # 1464 $UDP{id}{$session_id}{RawB} .= $udp_data; 1465 1466 # 1467 # Populate %UDP{id}{}{BothHTML} with coloured HTML 1468 # 2-way traffic, red for client to server 1469 # 1470 $copy = &Desex_HTML($udp_data); 1471 $UDP{id}{$session_id}{BothHTML} .= 1472 "<font color=\"red\">$copy</font>"; 1473 } 1474 # 1475 # Populate %Hex{UDP}{} with data necessary to generate coloured HTML 2-way 1476 # traffic, if needed. 1477 # 1478 if ($Arg{output_hex}) { 1479 push(@{$Hex{"UDP"}{$session_id}}, [$from_server, $udp_data]); 1480 } 1481 1482 # 1483 # Populate %UDP{id}{}{time}{} with raw 1-way traffic by time 1484 # 1485 $UDP{id}{$session_id}{time}{$time} .= $udp_data; 1486 1487 ### Store the packet timestamp for the first seen packet 1488 if (! defined $UDP{id}{$session_id}{StartTime}) { 1489 $UDP{id}{$session_id}{StartTime} = $time; 1490 1491 ### Store other info once 1492 if ($from_server) { 1493 $UDP{id}{$session_id}{src} = $ip_dest; 1494 $UDP{id}{$session_id}{dest} = $ip_src; 1495 $UDP{id}{$session_id}{src_port} = $udp_dest_port; 1496 $UDP{id}{$session_id}{dest_port} = $udp_src_port; 1497 } else { 1498 $UDP{id}{$session_id}{src} = $ip_src; 1499 $UDP{id}{$session_id}{dest} = $ip_dest; 1500 $UDP{id}{$session_id}{src_port} = $udp_src_port; 1501 $UDP{id}{$session_id}{dest_port} = $udp_dest_port; 1502 } 1503 } 1504 1505 ### Store the packet timestamp in case this is the last packet 1506 $UDP{id}{$session_id}{EndTime} = $time; 1507 1508 ### Print status line 1509 printf "%6s %-45s %s\n",$packet,$session_id,$length 1510 if $Arg{debug}; 1511 1512} 1513 1514 1515 1516# Process_ICMP_Packet - process a ICMP packet and store it in memory. It takes 1517# the raw ip data and populates the data structure %ICMP. 1518# time is the session_id. 1519# 1520sub Process_ICMP_Packet { 1521 1522 my $ip_data = shift; 1523 my $ip_src = shift; 1524 my $ip_dest = shift; 1525 my $time = shift; 1526 my $drops = shift; 1527 my $ver = shift; 1528 1529 #------------------------------------------------------------------- 1530 # 1531 # ICMP 1532 # 1533 1534 ### Unpack ICMP data 1535 ($icmp_type,$icmp_code,$icmp_cksum,$icmp_rest) = 1536 unpack('CCna*',$ip_data); 1537 1538 # 1539 # --- Store ICMP data in memory --- 1540 # 1541 1542 ### Store Fields 1543 $ICMP{time}{$time}{type} = $icmp_type; 1544 $ICMP{time}{$time}{code} = $icmp_code; 1545 $ICMP{time}{$time}{src} = $ip_src; 1546 $ICMP{time}{$time}{dest} = $ip_dest; 1547 $ICMP{time}{$time}{ver} = $ver; 1548 1549 # 1550 # Flag this session as a Partial if either tcpdump or snoop 1551 # confesses to dropping packets. 1552 # 1553 $ICMP{time}{$time}{Partial}++ if $drops; 1554 1555 # 1556 # Save data if ICMP echo/reply 1557 # 1558 if (($icmp_type == 0) || ($icmp_type == 8) || 1559 ($icmp_type == 128) || ($icmp_type == 129) || 1) { 1560 ### Unpack some more 1561 ($icmp_type,$icmp_code,$icmp_cksum,$icmp_id,$icmp_seq, 1562 $icmp_data) = unpack('CCnnna*',$ip_data); 1563 ### Save extra fields 1564 $ICMP{time}{$time}{id} = $icmp_id; 1565 $ICMP{time}{$time}{seq} = $icmp_seq; 1566 $ICMP{time}{$time}{data} = $icmp_data; 1567 } 1568 1569 ### Store size 1570 $ICMP{time}{$time}{size} += length($icmp_data); 1571 1572 if ($icmp_data ne "") { 1573 # 1574 # Populate %ICMP{time}{}{BothHTML} with coloured HTML 1575 # 1-way traffic, blue 1576 # 1577 $copy = &Desex_HTML($icmp_data); 1578 $ICMP{time}{$time}{BothHTML} .= 1579 "<font color=\"blue\">$copy</font>"; 1580 } 1581 1582 # 1583 # Populate %Hex{ICMP}{} with data necessary to generate coloured HTML 1584 # traffic, if needed. $from_server is always 1. session_id is time 1585 # 1586 if ($Arg{output_hex}) { 1587 push(@{$Hex{"ICMP"}{$time}}, [1, $icmp_data]); 1588 } 1589 1590 ### Print status line 1591 printf "%6s %-45s %s\n",$packet,"$ip_src,$ip_dest",$length 1592 if $Arg{debug}; 1593} 1594 1595 1596 1597# Process_TCP_Sessions - this subroutine processes %TCP, saving the 1598# sessions to various "session*" files on disk. It populates %Index 1599# with information on files that it has created. It also checks 1600# the application port numbers and triggers further processing - 1601# eg telnet replay files. Min/Max size checks are also done here. 1602# 1603sub Process_TCP_Sessions { 1604 1605 my ($filename,$id_text,$id_html,$rawboth,$time,$raw); 1606 my @Time; 1607 1608 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 1609 $Bench{$BM}{text} = "Process TCP Sessions - start"; 1610 1611 # 1612 # Loop through all TCP sessions 1613 # 1614 foreach $session_id (keys %{$TCP{id}}) { 1615 $number = $Index{Sort_Lookup}{"TCP:$session_id"}; 1616 1617 # 1618 # Determine the service - usually by the lowest numbered port, eg, 1619 # ports 51321 and 23 would give 23 (telnet). 1620 # 1621 $ip_src = $TCP{id}{$session_id}{src}; 1622 $ip_dest = $TCP{id}{$session_id}{dest}; 1623 $tcp_src_port = $TCP{id}{$session_id}{src_port}; 1624 $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; 1625 ($service,$client) = &Pick_Service_Port("TCP",$session_id, 1626 $tcp_src_port,$tcp_dest_port); 1627 1628 ### Fetch text name for this port 1629 $service_name = $Services_TCP{$service} || $service || "0"; 1630 1631 # 1632 # Don't actually save any files if CLI args say not to 1633 # 1634 if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; } 1635 if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; } 1636 if ($Arg{ip_reject}) { 1637 if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) { 1638 next; 1639 } 1640 } 1641 if ($Arg{ip_accept}) { 1642 unless ($Arg{IP_Accepted}{$ip_src} || 1643 $Arg{IP_Accepted}{$ip_dest}) { 1644 next; 1645 } 1646 } 1647 1648 # 1649 # --- Fetch RawBoth --- 1650 # 1651 # rawboth will contain the raw data in time order. 1652 $rawboth = ""; 1653 foreach $time (sort {$a <=> $b} 1654 (keys (%{$TCP{id}{$session_id}{time}}))) { 1655 $rawboth .= $TCP{id}{$session_id}{time}{$time}{data}; 1656 } 1657 $length = length($rawboth); 1658 1659 # 1660 # --- Check for Min and Max size --- 1661 # 1662 next if $length < $Arg{minbytes}; 1663 next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); 1664 1665 ### Print status line 1666 $numtext = sprintf("%04d",$number); 1667 printf "%6s %-45s %s\n",$numtext,$session_id,$service_name 1668 unless $Arg{quiet}; 1669 1670 1671 # 1672 # --- Save Info File to Disk --- 1673 # 1674 if ($Arg{output_info}) { 1675 $filename = "session_${numtext}.info"; 1676 $firsttime = localtime($TCP{id}{$session_id}{StartTime}); 1677 $lasttime = localtime($TCP{id}{$session_id}{EndTime}); 1678 $duration = ($TCP{id}{$session_id}{EndTime} - 1679 $TCP{id}{$session_id}{StartTime}); 1680 $duration = sprintf("%.0f",$duration); 1681 if ($TCP{id}{$session_id}{Partial}) { $partial = "yes"; } 1682 else { $partial = "no"; } 1683 1684 ### Build output text 1685 $outtext = "$numtext===$session_id===$service===" . 1686 "$service_name===$length\n\n" . 1687 "Source addr : $ip_src\n" . 1688 "Source port : $tcp_src_port\n" . 1689 "Dest addr : $ip_dest\n" . 1690 "Dest port : $tcp_dest_port\n" . 1691 "Dest service: $service_name\n" . 1692 "Length bytes: $length\n" . 1693 "First time : $firsttime\n" . 1694 "Last time : $lasttime\n" . 1695 "Duration : $duration seconds\n" . 1696 "Partial : $partial\n"; 1697 1698 ### Write info file 1699 open (OUT,">$filename") || 1700 die "ERROR11: creating $filename $!\n"; 1701 print OUT $outtext; 1702 close OUT; 1703 } 1704 1705 1706 # 1707 # --- Save Index data to Memory --- 1708 # 1709 1710 ## Fetch times 1711 $starttime = scalar localtime($TCP{id}{$session_id}{StartTime}); 1712 $duration = ($TCP{id}{$session_id}{EndTime} - 1713 $TCP{id}{$session_id}{StartTime}); 1714 $duration = sprintf("%.0f",$duration); 1715 1716 ### Generate session strings 1717 ($id_text,$id_html) = &Generate_TCP_IDs($session_id); 1718 1719 ### Construct HTML table row containing session data 1720 # JL: Added id attribute as link target 1721 $Index{HTML}[$number] = "<tr id=\"$number\">" . 1722 "<td><i>$number.</i></td>" . 1723 "<td><b>$starttime</b></td><td>$duration s</td><td> " . 1724 "<font color=\"blue\">$id_html " . 1725 "</font></td><td> <font color=\"red\">" . 1726 "$service_name</font></td><td> <font color=\"green\"> " . 1727 "$length bytes</font></td><td>\n"; 1728 1729 ### Construct text line containing session data 1730 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, 1731 $id_text,"($service_name)",$length); 1732 1733 ### Construct image info line (in case it is needed) 1734 $Image{HTML}[$number]{info} = "<tr><td><i>$number.</i>" . 1735 "</td><td><b>$starttime</b></td><td> " . 1736 "<font color=\"blue\">$id_html </font></td><td><td>\n"; 1737 1738 ### JL: Construct external image info line (in case it is needed) 1739 $ExtImage{HTML}[$number]{info} = "<tr><td><i>$number.</i>" . 1740 "</td><td><b>$starttime</b></td><td> " . 1741 "<font color=\"blue\">$id_html </font></td><td><td>\n"; 1742 1743 ### Construct GETPOST info line (in case it is needed) 1744 # starttime and host:port... are formatted differently so that 1745 # they are narrow and leave more room for the sub table. 1746 $GETPOST{HTML}[$number]{info} = "<tr><td><i>$number.</i>" . 1747 "</td><td><b>$starttime</b></td><td> " . 1748 "<font color=\"blue\">$id_html </font></td><td><td>\n"; 1749 1750 1751 # 1752 # --- Save Raw Sessions to Disk --- 1753 # 1754 1755 if ($Arg{output_raw}) { 1756 1757 # 1758 # Save ".raw" file, all raw 2-way data time-sorted. 1759 # 1760 $filename = "session_${numtext}.${service_name}.raw"; 1761 open (OUT,">$filename") || 1762 die "ERROR12: creating $filename $!\n"; 1763 binmode(OUT); # for backward OSs 1764 print OUT $rawboth; 1765 close OUT; 1766 1767 ### Update HTML index table with link 1768 $Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> "; 1769 1770 # 1771 # Save ".raw1" file, server->client 1-way data assembled. 1772 # 1773 $filename = "session_${numtext}.${service_name}.raw1"; 1774 open (OUT,">$filename") || 1775 die "ERROR13: creating $filename $!\n"; 1776 binmode(OUT); # for backward OSs 1777 print OUT &TCP_Follow_RawA($session_id); 1778 close OUT; 1779 1780 ### Update HTML index table with link 1781 $Index{HTML}[$number] .= "<a href=\"$filename\">raw1</a> "; 1782 1783 # 1784 # Save ".raw2" file, client->server 1-way data assembled. 1785 # 1786 $filename = "session_${numtext}.${service_name}.raw2"; 1787 open (OUT,">$filename") || 1788 die "ERROR14: creating $filename $!\n"; 1789 binmode(OUT); # for backward OSs 1790 print OUT &TCP_Follow_RawB($session_id); 1791 close OUT; 1792 1793 ### Update HTML index table with link 1794 $Index{HTML}[$number] .= "<a href=\"$filename\">raw2</a></li> "; 1795 } 1796 1797 next unless $Arg{output_apps}; 1798 1799 # 1800 # --- Save Session as HTML --- 1801 # 1802 if ($Arg{Save_As_TCP_HTML}{$service} || $Arg{output_allhtml}) { 1803 &Save_Both_HTML("TCP",$session_id,$number,$service_name, 1804 $id_html); 1805 } 1806 1807 # 1808 # --- Save X11 Session as HTML --- 1809 # 1810 if ($Arg{Save_As_X11_HTML}{$service}) { 1811 # 1812 # HTML Postprocessing can go here 1813 # 1814 &Generate_X11_HTML($session_id); 1815 &Process_BothHTML("TCP",$session_id,1); 1816 1817 &Save_Both_HTML("TCP",$session_id,$number,"text$service_name", 1818 $id_html); 1819 } 1820 1821 1822 # 1823 # --- Save Hex Dump as HTML --- 1824 # 1825 if ($Arg{output_hex}) { 1826 my ($text, $html) = &Process_Hex("TCP", $session_id); 1827 &Save_Hex_Text("TCP", $session_id, $number, $service_name, 1828 $id_text, $text); 1829 &Save_Hex_HTML("TCP", $session_id, $number, $service_name, 1830 $id_html, $html); 1831 } 1832 1833 # 1834 # --- Process Application Data --- 1835 # 1836 1837 if ($service == 20) { 1838 &Save_FTP_File($session_id,$number); 1839 } 1840 if ($service == 22) { 1841 &Save_Session_textSSH_files($session_id,$number, 1842 "SSH",$id_html); 1843 } 1844 if ($Arg{keydata} && $Arg{Save_As_TCP_Playback}{$service}) { 1845 # The following is for special analysis, 1846 &Save_Session_Keydata($session_id,$number, 1847 $service_name,$id_html); 1848 } 1849 if ($service == 25) { 1850 &Save_SMTP_Emails($session_id,$number); 1851 } 1852 if ($service == 80 or $service == 8080 or 1853 $service == 3127 or $service == 1080 or 1854 # JL: 8118 is HTTP(S) via polipo. 1855 # 9050 is Tor (socks4a, but works good enough for me). 1856 $service == 8118 or $service == 9050) { 1857 &Save_HTTP_Files($session_id,$number,$service_name); 1858 &Process_HTTP($session_id,$number); 1859 } 1860 1861 if ($Arg{Save_As_X11_Playback}{$service}) { 1862 &Save_Session_XReplay($session_id,$number,$service_name); 1863 } 1864 1865 if ($Arg{Save_As_VNC_Playback}{$service}) { 1866 &Save_Session_VNCReplay_andHTML($session_id,$number, 1867 $service_name,$id_html); 1868 } 1869 1870 $raw = &TCP_Follow_RawB($session_id); 1871 if ($raw =~ /^\200\0\0p0\211/) { 1872 &Save_NFS_File($session_id,$number); 1873 } 1874 1875 if ($Arg{Save_As_TCP_Playback}{$service}) { 1876 &Save_Session_Replay($session_id,$number,$service_name); 1877 } 1878 } 1879 1880 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 1881 $Bench{$BM}{text} = "Process TCP Sessions - end"; 1882} 1883 1884 1885# Process_UDP_Streams - this subroutine processes %UDP, saving the 1886# sessions to various "session*" files on disk. It populates %Index 1887# with information on the files that were created. It also checks 1888# the application port numbers and triggers further processing - 1889# eg DNS html output files. 1890# 1891sub Process_UDP_Streams { 1892 1893 my ($filename,$id_html,$id_text,$time,$rawboth); 1894 1895 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 1896 $Bench{$BM}{text} = "Process UDP Sessions - start"; 1897 1898 # 1899 # Loop through all UDP Streams 1900 # 1901 foreach $session_id (keys %{$UDP{id}}) { 1902 $number = $Index{Sort_Lookup}{"UDP:$session_id"}; 1903 1904 # 1905 # Determine the service - usually by the lowest numbered port, eg, 1906 # ports 51327 and 53 would give 53 (dns). (big assumption!) 1907 # 1908 $ip_src = $UDP{id}{$session_id}{src}; 1909 $ip_dest = $UDP{id}{$session_id}{dest}; 1910 $udp_src_port = $UDP{id}{$session_id}{src_port}; 1911 $udp_dest_port = $UDP{id}{$session_id}{dest_port}; 1912 ($service,$client) = &Pick_Service_Port("UDP",$session_id, 1913 $udp_src_port,$udp_dest_port); 1914 1915 ### Fetch text name for this port 1916 $service_name = $Services_UDP{$service} || $service || "0"; 1917 1918 # 1919 # Don't actually save any files if CLI args say not to 1920 # 1921 if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; } 1922 if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; } 1923 if ($Arg{ip_reject}) { 1924 if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) { 1925 next; 1926 } 1927 } 1928 if ($Arg{ip_accept}) { 1929 unless ($Arg{IP_Accepted}{$ip_src} || 1930 $Arg{IP_Accepted}{$ip_dest}) { 1931 next; 1932 } 1933 } 1934 1935 # 1936 # --- Fetch RawBoth --- 1937 # 1938 # rawboth will contain the raw data in time order. 1939 $rawboth = ""; 1940 foreach $time (sort {$a <=> $b} 1941 (keys (%{$UDP{id}{$session_id}{time}}))) { 1942 $rawboth .= $UDP{id}{$session_id}{time}{$time}; 1943 } 1944 $length = length($rawboth); 1945 1946 # 1947 # --- Check for Min and Max Size --- 1948 # 1949 next if $length < $Arg{minbytes}; 1950 next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); 1951 1952 ### Print status line 1953 $numtext = sprintf("%04d",$number); 1954 printf "%6s %-45s %s\n",$numtext,$session_id,$service_name 1955 unless $Arg{quiet}; 1956 1957 # 1958 # --- Save Info File to Disk --- 1959 # 1960 if ($Arg{output_info}) { 1961 $filename = "stream_${numtext}.info"; 1962 $firsttime = localtime($UDP{id}{$session_id}{StartTime}); 1963 $lasttime = localtime($UDP{id}{$session_id}{EndTime}); 1964 $duration = ($UDP{id}{$session_id}{EndTime} - 1965 $UDP{id}{$session_id}{StartTime}); 1966 $duration = sprintf("%.0f",$duration); 1967 if ($UDP{id}{$session_id}{Partial}) { $partial = "yes"; } 1968 else { $partial = "no"; } 1969 1970 ### Build output text 1971 $outtext = "$numtext===$session_id===$service===" . 1972 "$service_name===$length\n\n" . 1973 "Source addr : $ip_src\n" . 1974 "Source port : $udp_src_port\n" . 1975 "Dest addr : $ip_dest\n" . 1976 "Dest port : $udp_dest_port\n" . 1977 "Dest service: $service_name\n" . 1978 "Length bytes: $length\n" . 1979 "First time : $firsttime\n" . 1980 "Last time : $lasttime\n" . 1981 "Duration : $duration seconds\n" . 1982 "Partial : $partial\n"; 1983 1984 ### Write info file 1985 open (OUT,">$filename") || 1986 die "ERROR15: creating $filename $!\n"; 1987 print OUT $outtext; 1988 close OUT; 1989 } 1990 1991 1992 # 1993 # --- Save Index data in Memory --- 1994 # 1995 1996 ### Fetch Times 1997 $starttime = scalar localtime($UDP{id}{$session_id}{StartTime}); 1998 $duration = ($UDP{id}{$session_id}{EndTime} - 1999 $UDP{id}{$session_id}{StartTime}); 2000 $duration = sprintf("%.0f",$duration); 2001 2002 ### Construct HTML table row containing stream data 2003 if ($Arg{prefer_dns}) { 2004 $ip_src = &Get_Name_For_IP($ip_src); 2005 $ip_dest = &Get_Name_For_IP($ip_dest); 2006 } 2007 $id_html = "$ip_src:$udp_src_port <-> $ip_dest:$udp_dest_port"; 2008 $Index{HTML}[$number] = "<tr><td><i>$number.</i></td>" . 2009 "<td><b>$starttime</b></td><td>$duration s</td><td> " . 2010 "<font color=\"blue\">$id_html " . 2011 "</font></td><td> <font color=\"red\">" . 2012 "<i>$service_name</i></font></td><td> <font color=\"green\"> " . 2013 "$length bytes</font></td><td>\n"; 2014 2015 ### Construct text line containing session data 2016 $id_text = "$ip_src:$udp_src_port <-> $ip_dest:$udp_dest_port"; 2017 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, 2018 $id_text,"($service_name)",$length); 2019 2020 2021 # 2022 # --- Save Raw Stream to Disk --- 2023 # 2024 2025 if ($Arg{output_raw}) { 2026 2027 # 2028 # Save ".raw" file, all raw 2-way data time-sorted. 2029 # 2030 $filename = "stream_${numtext}.${service_name}.raw"; 2031 open (OUT,">$filename") || 2032 die "ERROR16: creating $filename $!\n"; 2033 binmode(OUT); # for backward OSs 2034 print OUT $rawboth; 2035 close OUT; 2036 2037 ### Update HTML index table with link 2038 $Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> "; 2039 2040 # 2041 # Save ".raw1" file, server->client 1-way data time-sorted. 2042 # 2043 $filename = "stream_${numtext}.${service_name}.raw1"; 2044 open (OUT,">$filename") || 2045 die "ERROR17: creating $filename $!\n"; 2046 binmode(OUT); # for backward OSs 2047 print OUT $UDP{id}{$session_id}{RawA}; 2048 close OUT; 2049 2050 ### Update HTML index table with link 2051 $Index{HTML}[$number] .= "<a href=\"$filename\">raw1</a> "; 2052 2053 # 2054 # Save ".raw2" file, client->server 1-way data time-sorted. 2055 # 2056 $filename = "stream_${numtext}.${service_name}.raw2"; 2057 open (OUT,">$filename") || 2058 die "ERROR18: creating $filename $!\n"; 2059 binmode(OUT); # for backward OSs 2060 print OUT $UDP{id}{$session_id}{RawB}; 2061 close OUT; 2062 2063 ### Update HTML index table with link 2064 $Index{HTML}[$number] .= "<a href=\"$filename\">raw2</a></li> "; 2065 } 2066 2067 next unless $Arg{output_apps}; 2068 2069 # 2070 # --- Save Stream as HTML --- 2071 # 2072 2073 if ($Arg{Save_As_UDP_HTML}{$service} || $Arg{output_allhtml}) { 2074 # 2075 # HTML Postprocessing can go here 2076 # 2077 &Process_BothHTML("UDP",$session_id); 2078 2079 &Save_Both_HTML("UDP",$session_id,$number,$service_name); 2080 } 2081 2082 # 2083 # --- Save Hex Dump as HTML --- 2084 # 2085 if ($Arg{output_hex}) { 2086 my ($text, $html) = &Process_Hex("UDP", $session_id); 2087 &Save_Hex_Text("UDP", $session_id, $number, $service_name, 2088 $id_text, $text); 2089 &Save_Hex_HTML("UDP", $session_id, $number, $service_name, 2090 $id_text, $html); 2091 } 2092 2093 2094 # 2095 # --- Process Application Data --- 2096 # 2097 if ($Arg{Save_As_UDP_Playback}{$service}) { 2098 &Save_Stream_Replay($session_id,$number,$service_name); 2099 } 2100 2101 if ($service == 53) { 2102 &Save_DNS_File($session_id,$number); 2103 } 2104 2105 } 2106 2107 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 2108 $Bench{$BM}{text} = "Process UDP Sessions - end"; 2109} 2110 2111 2112 2113# Process_ICMP - this subroutine processes %ICMP. 2114# 2115sub Process_ICMP { 2116 2117 my ($filename,$id_text,$id_html); 2118 2119 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 2120 $Bench{$BM}{text} = "Process ICMP Sessions - start"; 2121 2122 # 2123 # Loop through all ICMP Streams 2124 # 2125 foreach $time (keys %{$ICMP{time}}) { 2126 $number = $Index{Sort_Lookup}{"ICMP:$time"}; 2127 2128 2129 ### Fetch Data 2130 $icmp_type = $ICMP{time}{$time}{type}; 2131 $icmp_code = $ICMP{time}{$time}{code}; 2132 $icmp_ver = $ICMP{time}{$time}{ver}; 2133 $ip_src = $ICMP{time}{$time}{src}; 2134 $ip_dest = $ICMP{time}{$time}{dest}; 2135 $session_id = "$ip_src,$ip_dest"; 2136 2137 ### Fetch text name for this port 2138 $type_name = $ICMP_Types{$icmp_type} || $icmp_type || "0"; 2139 $service_name = $icmp_type; 2140 2141 # 2142 # Don't actually save any files if CLI args say not to 2143 # 2144 if ($Arg{ip_reject}) { 2145 if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}){ 2146 next; 2147 } 2148 } 2149 if ($Arg{ip_accept}) { 2150 unless ($Arg{IP_Accepted}{$ip_src} || 2151 $Arg{IP_Accepted}{$ip_dest}) { 2152 next; 2153 } 2154 } 2155 2156 # 2157 # --- Check for Min and Max Size --- 2158 # 2159 $length = length($ICMP{time}{$time}{data}); 2160 next if $length < $Arg{minbytes}; 2161 next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes})); 2162 2163 ### Print status line 2164 $numtext = sprintf("%04d",$number); 2165 printf "%6s %-45s ICMP %s\n",$numtext,$session_id,$type_name 2166 unless $Arg{quiet}; 2167 2168 # 2169 # --- Save Info File to Disk --- 2170 # 2171 if (($Arg{output_info}) && ($length > 0)) { 2172 $filename = "icmp_${numtext}.${service_name}.info"; 2173 if ($ICMP{time}{$time}{Partial}) { $partial = "yes"; } 2174 else { $partial = "no"; } 2175 $starttime = scalar localtime($time); 2176 2177 ### Build output text 2178 $outtext = "$numtext===$session_id===$icmp_type===" . 2179 "$type_name===$length\n\n" . 2180 "Source addr : $ip_src\n" . 2181 "Dest addr : $ip_dest\n" . 2182 "ICMP version: $icmp_ver\n" . 2183 "ICMP type : $icmp_type\n" . 2184 "ICMP code : $icmp_code\n" . 2185 "ICMP name : $type_name\n" . 2186 "Length bytes: $length\n" . 2187 "Time : $starttime\n" . 2188 "Partial : $partial\n"; 2189 2190 ### Write info file 2191 open (OUT,">$filename") || 2192 die "ERROR19: creating $filename $!\n"; 2193 print OUT $outtext; 2194 close OUT; 2195 } 2196 2197 2198 # 2199 # --- Save Index data in Memory --- 2200 # 2201 2202 ### Fetch Times 2203 $starttime = scalar localtime($time); 2204 2205 ### Construct HTML table row containing stream data 2206 $id_html = "$ip_src -> $ip_dest"; 2207 $Index{HTML}[$number] = "<tr><td><i>$number.</i></td>" . 2208 "<td><b>$starttime</b></td><td>0 s</td><td> " . 2209 "<font color=\"blue\">$id_html" . 2210 "</font></td><td> <font color=\"red\">" . 2211 "<i>$icmp_ver</i></font></td><td> <font color=\"green\"> " . 2212 "$length bytes</font></td><td>$type_name\n"; 2213 2214 ### Construct text line containing session data 2215 $id_text = "$ip_src -> $ip_dest"; 2216 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number, 2217 $id_text, "($icmp_ver $type_name)",$length); 2218 2219 2220 # 2221 # --- Save Raw Stream to Disk --- 2222 # 2223 2224 if (($Arg{output_raw}) && ($length > 0)) { 2225 2226 # 2227 # Save ".raw" file, all raw 2-way data time-sorted. 2228 # 2229 $filename = "icmp_${numtext}.${service_name}.raw"; 2230 open (OUT,">$filename") || 2231 die "ERROR20: creating $filename $!\n"; 2232 binmode(OUT); # for backward OSs 2233 print OUT $ICMP{time}{$time}{data}; 2234 close OUT; 2235 2236 ### Update HTML index table with link 2237 $Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> "; 2238 2239 } 2240 2241 # 2242 # --- Save Stream as HTML --- 2243 # 2244 2245 if ($Arg{output_allhtml}) { 2246 # 2247 # HTML Postprocessing can go here 2248 # 2249 &Process_BothHTML("ICMP",$time); 2250 2251 &Save_Both_HTML("ICMP",$time,$number,$service_name,$id_html); 2252 } 2253 2254 # 2255 # --- Save Hex Dump as HTML --- 2256 # 2257 if ($Arg{output_hex}) { 2258 my ($text, $html) = &Process_Hex("ICMP", $time); 2259 &Save_Hex_Text("ICMP", $session_id, $number, $service_name, 2260 $id_text, $text); 2261 &Save_Hex_HTML("ICMP", $session_id, $number, $service_name, 2262 $id_html, $html); 2263 } 2264 } 2265 2266 $Bench{++$BM}{mark} = new Benchmark if $Arg{bench}; 2267 $Bench{$BM}{text} = "Process ICMP Sessions - end"; 2268} 2269 2270 2271# JL: Process_DNS - DNS processing. Look for DNS replies and store 2272# names for IP addresses into %DNS. 2273# Also store CNAME aliases so that the "original" name can be retrieved. 2274# 2275sub Process_DNS { 2276 my $data = shift; 2277 my $session_id = shift; 2278 2279 my $dns = Net::DNS::Packet->new(\$data); 2280 2281 unless ($dns) { 2282 #print "Failed to create Net::DNS::Packet!\n"; 2283 return; 2284 } 2285 2286 $UDP{id}{$session_id}{DNS} = $dns->string; 2287 foreach my $rr ($dns->answer) { 2288 if ($rr->type eq "A") { 2289 $DNS{$rr->address} = $rr->name; 2290 } 2291 if ($rr->type eq "CNAME") { 2292 $DNS{$rr->cname} = $rr->name; 2293 } 2294 } 2295} 2296 2297 2298 2299# Process_HTTP - HTTP processing. Looks for GETs and POSTs, and process them 2300# into %GETPOST. Constructs a HTTP log in %HTTPlog. 2301# JL: Added host parameter 2302# 2303sub Process_HTTP { 2304 my ($junk,$var,$value,$term,$data,$request,$host,$site,$post,$get,$reply); 2305 my ($start,$src,$num,$req,$recv,$type,$status,$time1,$duration,$dest); 2306 my @Terms; 2307 my $index = 0; 2308 my $indexA = 0; 2309 my $indexB = 0; 2310 2311 ### Input 2312 my $session_id = shift; 2313 my $number = shift; 2314 my $partnum = 0; 2315 2316 $src = $TCP{id}{$session_id}{src}; 2317 $dest = $TCP{id}{$session_id}{dest}; 2318 2319 # 2320 # Process 2321 # 2322 2323 ### Get packet times (may need to use seqs instead) 2324 @Times = sort{$a <=> $b} (keys(%{$TCP{id}{$session_id}{time}})); 2325 2326 ### Step through each packet 2327 for ($i=0; $i <= $#Times; $i++) { 2328 2329 ### Fetch data from mem 2330 $time = $Times[$i]; 2331 $request = $TCP{id}{$session_id}{time}{$time}{data}; 2332 $request =~ s/^\0\0*//; 2333 2334 # 2335 # --- Do HTTPlog Processing --- 2336 # 2337 2338 next unless $request =~ /^(GET|POST)\s/; # speed 2339 2340 ### Calc duration 2341 $time1 = $Times[$i+1] || $time; 2342 $duration = $time1 - $time; 2343 2344 # some magic 2345 $reply = ""; 2346 foreach $inc (1..16) { 2347 $next = $TCP{id}{$session_id}{time}{$Times[$i+$inc]}{data}; 2348 $next =~ s/^\0\0*//; 2349 if ($next =~ /^U*\0*HTTP/) { 2350 $reply = $next; 2351 $time1 = $Times[$i+$inc] || $time; 2352 $duration = $time1 - $time; 2353 last; 2354 } else { 2355 $request .= $next; 2356 } 2357 } 2358 $i++; # speed 2359 $partnum++; 2360 if ($request =~ /^GET \S* HTTP/) { 2361 2362 ### JL: Get the host string, referer, and cookies. 2363 ($host) = $request =~ /\sHost:\s(\S*)\s/is; 2364 ($referer) = $request =~ /\sReferer:\s(\S*)/is; 2365 ($cookie) = $request =~ /\sCookie:\s(\S*)/is; 2366 ($setcookie) = $reply =~ /\sSet-Cookie:\s(\S*)/is; 2367 2368 ### Get the site string 2369 ($site) = $request =~ /^GET (\S*)\s/; 2370 if ($site =~ m:^/:) { 2371 # assume this was a http, missing the "http://host" 2372 # JL: Prefer hostname over IP address 2373 if ($Arg{httplog_html}) { 2374 $site = "http://${host}$site"; 2375 } else { 2376 $site = "http://${dest}$site"; 2377 } 2378 } 2379 2380 ### Get the status and mime type from reply 2381 ($status) = $reply =~ /HTTP\/\S*\s(\S*)/s; 2382 # JL: Be careful to use case insensitive matching 2383 ($type) = $reply =~ /Content-Type:\s(\S*)/is; 2384 ($size) = $reply =~ /Content-Length:\s(\S*)/is; 2385 $type = "-" if $type eq ""; 2386 $size = 0 if $size eq ""; 2387 2388 $result = $Result_Names{$status} || "TCP_HIT"; 2389 2390 ### Store the log entry 2391 $HTTPlog{time}{$time} = 2392 Print_Log_Line($number,$time,$duration, 2393 $src,$dest,$result,$status,$size, 2394 "GET",$site,"-","NONE","","-",$type); 2395 $HTTPtxtlog{time}{$time} = 2396 Print_TxtLog_Line($number,$time, 2397 $referer,$cookie,$setcookie, 2398 "GET",$site); 2399 $HTTPlog{notempty} = 1; 2400 2401 ### JL: External image data. 2402 if ( defined $ExtImage{HTML}[$number]{parts}[$partnum] ) { 2403 $ExtImage{HTML}[$number]{links} .= "<img src=\"$site\"> "; 2404 } 2405 } elsif ($request =~ /^POST .* HTTP/) { 2406 ### Get the site string 2407 ($site) = $request =~ /^POST (\S*)\s/; 2408 if ($site =~ m:^/:) { 2409 # assume this was a http, missing the "http://host" 2410 $site = "http://${dest}$site"; 2411 } 2412 ### JL: Get the host string, referer, and cookies. 2413 ($host) = $request =~ /\sHost:\s(\S*)\s/is; 2414 ($referer) = $request =~ /\sReferer:\s(\S*)/is; 2415 ($cookie) = $request =~ /\sCookie:\s(\S*)/is; 2416 ($setcookie) = $reply =~ /\sSet-Cookie:\s(\S*)/is; 2417 2418 ### Get the status and mime type 2419 ($status) = $reply =~ /HTTP\/\S*\s(\S*)/s; 2420 ($type) = $reply =~ /Content-Type:\s(\S*)/is; 2421 ($size) = $reply =~ /Content-Length:\s(\S*)/is; 2422 $type = "-" if $type eq ""; 2423 $size = length($TCP{id}{$session_id}) if $size eq ""; 2424 $result = $Result_Names{$status} || "TCP_HIT"; 2425 2426 ### Store the log entry 2427 $HTTPlog{time}{$time} = 2428 Print_Log_Line($number,$time,$duration, 2429 $src,$dest,$result,$status,$size, 2430 "POST",$site,"-","NONE","","-",$type); 2431 $HTTPtxtlog{time}{$time} = 2432 Print_TxtLog_Line($number,$time, 2433 $referer,$cookie,$setcookie, 2434 "POST",$site); 2435 $HTTPlog{notempty} = 1; 2436 2437 } 2438 2439 # 2440 # --- Do GETPOST Processing --- 2441 # 2442 # JL: chaosreader 0.94 includes only URIs containing a question 2443 # mark. Why? Go for all instead. 2444 #if ($request =~ /^GET \S*\?\S* HTTP/) { 2445 if ($request =~ /^GET \S* HTTP/) { 2446 2447 ### Get the GET string 2448 ($site,$get) = $request =~ /^GET (\S*)\?(\S*)\s/; 2449 if ($site eq "") { 2450 ($site) = $request =~ /^GET (\S*)\s/; 2451 } 2452 # check it looks like a GET, 2453 # JL: Why only those with parameters? 2454 #if ($get =~ /=/) { 2455 2456 # 2457 # Populate %GETPOST with a table containing the GET data 2458 # 2459 if (! defined $GETPOST{HTML}[$number]{query}) { 2460 $GETPOST{HTML}[$number]{info} .= 2461 "<font color=\"red\">GET</font></td><td width=70%>"; 2462 $GETPOST{notempty} = 1; 2463 } else { 2464 $GETPOST{HTML}[$number]{query} .= "<hr>\n"; 2465 } 2466 2467 # 2468 # Generate table of query key value pairs 2469 # 2470 $GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\n"; 2471 @Terms = split(/&/,$get); 2472 foreach $term (@Terms) { 2473 ($var,$value) = split(/=/,$term); 2474 $value =~ tr/+/ /; 2475 $value =~ s/%([a-f0-9][a-f0-9])/pack("C",hex($1))/egi; 2476 $value =~ s/</</g; 2477 $value =~ s/>/>/g; 2478 $value =~ s/\n/<br>\n/g; 2479 $GETPOST{HTML}[$number]{query} .= 2480 "<tr><td><b>$var</b></td>" . 2481 "<td><font face=\"Courier\">$value</font></td></tr>\n"; 2482 } 2483 $GETPOST{HTML}[$number]{query} .= "</table>\n"; 2484 #} 2485 2486 } elsif ($request =~ /^POST .* HTTP/) { 2487 2488 ### Get the POST strings 2489 ($junk,$post,$junk1) = split(/\n\n|\r\n\r\n/,$request); 2490 2491 # check it looks like a POST 2492 if ($post =~ /=/) { 2493 2494 # 2495 # Populate %GETPOST with a table containing the POST data 2496 # 2497 if (! defined $GETPOST{HTML}[$number]{query}) { 2498 $GETPOST{HTML}[$number]{info} .= 2499 "<font color=\"red\">POST</font></td><td width=70%>"; 2500 $GETPOST{notempty} = 1; 2501 } else { 2502 $GETPOST{HTML}[$number]{query} .= "<hr>\n"; 2503 } 2504 2505 ($site) = $request =~ /^POST (\S*)\s/; 2506 2507 $post =~ s/HTTP .*//s; 2508 2509 # 2510 # Generate table of query key value pairs 2511 # 2512 $GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\n"; 2513 @Terms = split(/&/,$post); 2514 foreach $term (@Terms) { 2515 ($var,$value) = split(/=/,$term); 2516 $value =~ tr/+/ /; 2517 $value =~ 2518 s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 2519 $value =~ s/</</g; 2520 $value =~ s/>/>/g; 2521 $value =~ s/\n/<br>/g; 2522 $GETPOST{HTML}[$number]{query} .= 2523 "<tr><td><b>$var</b></td>" . 2524 "<td><font face=\"Courier\">$value</font></td></tr>\n"; 2525 } 2526 $GETPOST{HTML}[$number]{query} .= "</table>\n"; 2527 } 2528 } 2529 } 2530} 2531 2532 2533# Sort_Index - this creates a sort order for the master index.html, based 2534# on the sort argument (defaults to sort by time). 2535# 2536sub Sort_Index { 2537 2538 if ($Arg{sort} eq "size") { 2539 &Sort_Index_By_Size(); 2540 } elsif ($Arg{sort} eq "type") { 2541 &Sort_Index_By_Type(); 2542 } elsif ($Arg{sort} eq "ip") { 2543 &Sort_Index_By_IP(); 2544 } else { 2545 &Sort_Index_By_Time(); 2546 } 2547} 2548 2549 2550# Sort_Index_By_Time - this calculates an appropriate order for the index 2551# files based on session start time. 2552# 2553sub Sort_Index_By_Time { 2554 my ($session_id,$time,$number); 2555 2556 # 2557 # Determine Session and Stream time order 2558 # 2559 foreach $session_id (keys %{$TCP{id}}) { 2560 $Index{Time_Order}{"TCP:$session_id"} = 2561 $TCP{id}{$session_id}{StartTime}; 2562 } 2563 foreach $session_id (keys %{$UDP{id}}) { 2564 $Index{Time_Order}{"UDP:$session_id"} = 2565 $UDP{id}{$session_id}{StartTime}; 2566 } 2567 foreach $time (keys %{$ICMP{time}}) { 2568 $Index{Time_Order}{"ICMP:$time"} = $time; 2569 } 2570 $number = 0; 2571 foreach $session (sort {$Index{Time_Order}{$a} <=> 2572 $Index{Time_Order}{$b}} keys %{$Index{Time_Order}}) { 2573 $number++; 2574 $Index{Sort_Lookup}{$session} = $number; 2575 } 2576} 2577 2578 2579# Sort_Index_By_Size - this calculates an appropriate order for the index 2580# files based on session size. 2581# 2582sub Sort_Index_By_Size { 2583 my ($session_id,$time,$number); 2584 2585 # 2586 # Determine Session and Stream size order 2587 # 2588 foreach $session_id (keys %{$TCP{id}}) { 2589 $Index{Size_Order}{"TCP:$session_id"} = 2590 $TCP{id}{$session_id}{size}; 2591 } 2592 foreach $session_id (keys %{$UDP{id}}) { 2593 $Index{Size_Order}{"UDP:$session_id"} = 2594 $UDP{id}{$session_id}{size}; 2595 } 2596 foreach $time (keys %{$ICMP{time}}) { 2597 $Index{Size_Order}{"ICMP:$time"} = 2598 $ICMP{time}{$time}{size}; 2599 } 2600 $number = 0; 2601 foreach $session (sort {$Index{Size_Order}{$b} <=> 2602 $Index{Size_Order}{$a}} keys %{$Index{Size_Order}}) { 2603 $number++; 2604 $Index{Sort_Lookup}{$session} = $number; 2605 } 2606} 2607 2608 2609# Sort_Index_By_Type - this calculates an appropriate order for the index 2610# files based on session type, followed by time. 2611# 2612sub Sort_Index_By_Type { 2613 my ($service,$tcp_src_port,$tcp_dest_port,$client,$udp_src_port, 2614 $udp_dest_port,$session_id,$time,$number); 2615 2616 # 2617 # Determine Session and Stream time order 2618 # 2619 foreach $session_id (keys %{$TCP{id}}) { 2620 # Determine the service - usually by the lowest numbered port 2621 $tcp_src_port = $TCP{id}{$session_id}{src_port}; 2622 $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; 2623 ($service,$client) = &Pick_Service_Port("TCP",$session_id, 2624 $tcp_src_port,$tcp_dest_port); 2625 2626 $Index{Type_Order}{"TCP:$session_id"}{1} = 1; 2627 $Index{Type_Order}{"TCP:$session_id"}{2} = $service; 2628 $Index{Type_Order}{"TCP:$session_id"}{3} = 2629 $TCP{id}{$session_id}{StartTime}; 2630 } 2631 foreach $session_id (keys %{$UDP{id}}) { 2632 # Determine the service - usually by the lowest numbered port 2633 $udp_src_port = $UDP{id}{$session_id}{src_port}; 2634 $udp_dest_port = $UDP{id}{$session_id}{dest_port}; 2635 ($service,$client) = &Pick_Service_Port("UDP",$session_id, 2636 $udp_src_port,$udp_dest_port); 2637 2638 $Index{Type_Order}{"UDP:$session_id"}{1} = 2; 2639 $Index{Type_Order}{"UDP:$session_id"}{2} = $service; 2640 $Index{Type_Order}{"UDP:$session_id"}{3} = 2641 $UDP{id}{$session_id}{StartTime}; 2642 } 2643 foreach $time (keys %{$ICMP{time}}) { 2644 $Index{Type_Order}{"ICMP:$time"}{1} = 3; 2645 $Index{Type_Order}{"ICMP:$time"}{2} = 0; 2646 $Index{Type_Order}{"ICMP:$time"}{3} = $time; 2647 } 2648 2649 # now we sort by TCP->UDP->IP then port then time. 2650 $number = 0; 2651 foreach $session (sort { 2652 $Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} || 2653 $Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} || 2654 $Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3} 2655 } keys %{$Index{Type_Order}}) { 2656 $number++; 2657 $Index{Sort_Lookup}{$session} = $number; 2658 } 2659} 2660 2661 2662# Sort_Index_By_IP - this calculates an appropriate order for the index 2663# files based on client IP, followed by time. 2664# 2665sub Sort_Index_By_IP { 2666 my ($service,$ip,$ip_dest,$ip_src,$client, 2667 $session_id,$time,$number,$text,$html,$rest); 2668 my @IP; 2669 2670 # 2671 # Determine Session and Stream time order 2672 # 2673 foreach $session_id (keys %{$TCP{id}}) { 2674 # Determine source IP 2675 # here we use the same subroutine as the index.html 2676 # so that they match up. 2677 ($text,$html) = &Generate_TCP_IDs($session_id); 2678 ($ip,$rest) = split(/:/,$text,2); 2679 2680 # Split on IPv4 or IPv6 2681 $IP = (); 2682 if ($ip =~ /\./) { @IP = split(/\./,$ip); } 2683 else { $IP[0] = $ip; } 2684 2685 $Index{Type_Order}{"TCP:$session_id"}{1} = $IP[0]; 2686 $Index{Type_Order}{"TCP:$session_id"}{2} = $IP[1]; 2687 $Index{Type_Order}{"TCP:$session_id"}{3} = $IP[2]; 2688 $Index{Type_Order}{"TCP:$session_id"}{4} = $IP[3]; 2689 $Index{Type_Order}{"TCP:$session_id"}{5} = 2690 $TCP{id}{$session_id}{StartTime}; 2691 } 2692 foreach $session_id (keys %{$UDP{id}}) { 2693 # Determine source IP 2694 $ip = $UDP{id}{$session_id}{src}; 2695 2696 # Split on IPv4 or IPv6 2697 $IP = (); 2698 if ($ip =~ /\./) { @IP = split(/\./,$ip); } 2699 else { $IP[0] = $ip; } 2700 2701 $Index{Type_Order}{"UDP:$session_id"}{1} = $IP[0]; 2702 $Index{Type_Order}{"UDP:$session_id"}{2} = $IP[1]; 2703 $Index{Type_Order}{"UDP:$session_id"}{3} = $IP[2]; 2704 $Index{Type_Order}{"UDP:$session_id"}{4} = $IP[3]; 2705 $Index{Type_Order}{"UDP:$session_id"}{5} = 2706 $UDP{id}{$session_id}{StartTime}; 2707 } 2708 foreach $time (keys %{$ICMP{time}}) { 2709 # Determine source IP 2710 $ip = $ICMP{time}{$time}{src}; 2711 2712 # Split on IPv4 or IPv6 2713 $IP = (); 2714 if ($ip =~ /\./) { @IP = split(/\./,$ip); } 2715 else { $IP[0] = $ip; } 2716 2717 $Index{Type_Order}{"ICMP:$time"}{1} = $IP[0]; 2718 $Index{Type_Order}{"ICMP:$time"}{2} = $IP[1]; 2719 $Index{Type_Order}{"ICMP:$time"}{3} = $IP[2]; 2720 $Index{Type_Order}{"ICMP:$time"}{4} = $IP[3]; 2721 $Index{Type_Order}{"ICMP:$time"}{5} = $time; 2722 } 2723 2724 # now we sort by IP then time 2725 $number = 0; 2726 foreach $session (sort { 2727 $Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} || 2728 $Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} || 2729 $Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3} || 2730 $Index{Type_Order}{$a}{4} <=> $Index{Type_Order}{$b}{4} || 2731 $Index{Type_Order}{$a}{1} cmp $Index{Type_Order}{$b}{1} || 2732 $Index{Type_Order}{$a}{5} <=> $Index{Type_Order}{$b}{5} 2733 } keys %{$Index{Type_Order}}) { 2734 $number++; 2735 $Index{Sort_Lookup}{$session} = $number; 2736 } 2737} 2738 2739 2740# Print_Welcome - print short program welcome message. 2741# 2742sub Print_Welcome { 2743 unless ($Arg{quiet}) { 2744 print "Chaosreader ver 0.95.10\n\n"; 2745 } 2746} 2747 2748 2749# Print_Header1 - print program welcome message. 2750# 2751sub Print_Header1 { 2752 unless ($Arg{quiet}) { 2753 print "Reading $TYPE log...\n"; 2754 printf "%6s %-45s %s\n","Packet", 2755 "Session (host:port <=> host:port)","Length"; 2756 } 2757} 2758 2759 2760# Print_Header2 - print header before loading the file 2761# 2762sub Print_Header2 { 2763 print "\nCreating files...\n" unless $Arg{quiet}; 2764 printf "%6s %-45s %s\n","Num","Session (host:port <=> host:port)", 2765 "Service" unless $Arg{quiet}; 2766} 2767 2768 2769# Print_Footer1 - print footer at end of program. 2770# 2771sub Print_Footer1 { 2772 if ($Arg{output_index}) { 2773 print "\nindex.html created.\n" unless $Arg{quiet}; 2774 } 2775} 2776 2777 2778# Chdir - change directory with error 2779# 2780sub Chdir { 2781 my $dir = shift; 2782 # 2783 # This can be invoked with $Arg{output_dir}, so $dir won't 2784 # always be defined - which is okay. 2785 # 2786 if (defined $dir) { 2787 chdir "$dir" || 2788 die "ERROR21: Can't cd to $dir: $!\n"; 2789 } 2790} 2791 2792 2793# Create_Index_Files - Create the HTML and text index files. This reads 2794# %Index and creates the files on disk. 2795# 2796sub Create_Index_Files { 2797 my ($html_index,$html_line,$html_links,$image_empty,$getpost_empty); 2798 $getpost_empty = $image_empty = ""; 2799 2800 if ($Arg{output_index}) { 2801 2802 2803 ###################### 2804 # --- index.html --- 2805 2806 $image_empty = "(Empty) " unless $Image{notempty}; 2807 $getpost_empty = "(Empty) " unless $GETPOST{notempty}; 2808 $httplog_empty = "(Empty) " unless $HTTPlog{notempty}; 2809 # 2810 # Create HTML Index file containing all reports 2811 # 2812 open(FILE,">index.html") || die "ERROR22: creating index: $!\n"; 2813 print FILE <<END_HTML; 2814<html> 2815<head><title>Chaosreader Report, $Arg{infile}</title></head> 2816<body bgcolor="white" textcolor="black"> 2817<font size=+3>Chaosreader Report</font><br> 2818<font size=+1>File: $Arg{infile}, Type: $TYPE, Created at: $the_date</font><p> 2819<a href="image.html"><font color="blue"><b>Image Report</b></font></a> 2820 $image_empty - Click here for a report on captured images.<br> 2821<a href="extimage.html"><font color="blue"><b>External Image Report</b></font></a> 2822 $image_empty - Click here for a report embedding external images.<br> 2823<a href="getpost.html"><font color="blue"><b>GET/POST Report</b></font></a> 2824 $getpost_empty - Click here for a report on HTTP GETs and POSTs.<br> 2825<a href="$Arg{httplog_name}"><font color="blue"><b>HTTP Proxy Log</b></font></a> 2826 $httplog_empty - Click here for a generated proxy style HTTP log.<br> 2827<a href="$Arg{httplog_txt}"><font color="blue"><b>New HTTP Proxy Log</b></font></a> 2828 $httplog_empty - Click here for HTTP log with referers and Cookie indicators.<p> 2829<font size=+2>TCP/UDP/... Sessions</font><br> 2830<table border=2> 2831END_HTML 2832 for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) { 2833 $html_line = $Index{HTML}[$html_index]; 2834 next unless defined $html_line; 2835 print FILE "$html_line </td></tr>\n"; 2836 } 2837 print FILE <<END_HTML; 2838</table><p> 2839<font size=+2>IP and MAC Count</font><br> 2840<table border=2> 2841<tr><th>IP</th><th>MAC</th><th>Count</th></tr> 2842END_HTML 2843 foreach $IP_MAC (sort {$Count{IP}{$b} <=> $Count{IP}{$a}} 2844 keys %{$Count{IP}}) { 2845 #print FILE "<tr><td>$IP</td><td>$Count{IP}{$IP}</td></tr>\n"; 2846 ($ip, $mac) = split(',', $IP_MAC); 2847 print FILE "<tr><td>$ip</td><td>$mac</td><td>$Count{IP}{$IP_MAC}</td></tr>\n"; 2848 } 2849 print FILE <<END_HTML; 2850</table><p> 2851<font size=+2>TCP Port Count</font><br> 2852<table border=2> 2853END_HTML 2854 foreach $port (sort {$Count{TCPport}{$b} <=> $Count{TCPport}{$a}} 2855 keys %{$Count{TCPport}}) { 2856 $port_text = $Services_TCP{$port} || $port || "0"; 2857 print FILE "<tr><td>$port_text</td><td>$Count{TCPport}{$port}" . 2858 "</td></tr>\n"; 2859 } 2860 print FILE <<END_HTML; 2861</table><p> 2862<font size=+2>UDP Port Count</font><br> 2863<table border=2> 2864END_HTML 2865 foreach $port (sort {$Count{UDPport}{$b} <=> $Count{UDPport}{$a}} 2866 keys %{$Count{UDPport}}) { 2867 $port_text = $Services_UDP{$port} || $port || "0"; 2868 print FILE "<tr><td>$port_text</td><td>$Count{UDPport}{$port}" . 2869 "</td></tr>\n"; 2870 } 2871 print FILE <<END_HTML; 2872</table><p> 2873<font size=+2>IP Protocol Count</font><br> 2874<table border=2> 2875END_HTML 2876 foreach $protocol (sort {$Count{IPprotocol}{$b} <=> 2877 $Count{IPprotocol}{$a}} keys %{$Count{IPprotocol}}) { 2878 $protocol_text = $IP_Protocols{$protocol}; 2879 print FILE "<tr><td>$protocol_text</td><td>" . 2880 "$Count{IPprotocol}{$protocol}</td></tr>\n"; 2881 } 2882 print FILE <<END_HTML; 2883</table><p> 2884<font size=+2>Ethernet Type Count</font><br> 2885<table border=2> 2886END_HTML 2887 foreach $type (sort {$Count{EtherType}{$b} <=> $Count{EtherType}{$a}} 2888 keys %{$Count{EtherType}}) { 2889 print FILE "<tr><td>$type</td><td>$Count{EtherType}{$type}" . 2890 "</td></tr>\n"; 2891 } 2892 print FILE <<END_HTML; 2893</table> 2894</body> 2895</html> 2896END_HTML 2897 2898 2899 ###################### 2900 # --- index.text --- 2901 2902 # 2903 # Create Text index file 2904 # 2905 open(FILE,">index.text") || die "ERROR23: creating index: $!\n"; 2906 print FILE "TCP/UDP/... Sessions\nFile: $Arg{infile}, " 2907 . "Type: $TYPE, Created at: $the_date\n\n"; 2908 print FILE @{$Index{Text}}; 2909 close FILE; 2910 2911 2912 ###################### 2913 # --- image.html --- 2914 2915 # 2916 # Create HTML Image Index file to display images 2917 # 2918 open(FILE,">image.html") || die "ERROR24: creating index: $!\n"; 2919 print FILE <<END_HTML; 2920<html> 2921<head><title>Chaosreader Image Report</title></head> 2922<body bgcolor="white" textcolor="black"> 2923<font size=+3>Chaosreader Image Report</font><br> 2924<font size=+1>Created at: $the_date, Type: $TYPE</font><p> 2925<font size=+2>Images</font><br> 2926<table border=2> 2927END_HTML 2928 for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) { 2929 $html_line = $Image{HTML}[$html_index]{info}; 2930 $html_links = $Image{HTML}[$html_index]{links}; 2931 next unless defined $html_links; 2932 print FILE "$html_line $html_links </td></tr>\n"; 2933 } 2934 print FILE <<END_HTML; 2935</table><p> 2936</body> 2937</html> 2938END_HTML 2939 2940 2941 ###################### 2942 # --- extimage.html --- 2943 2944 # 2945 # Create HTML External Image Index file to display images 2946 # 2947 open(FILE,">extimage.html") || die "ERROR24: creating index: $!\n"; 2948 print FILE <<END_HTML; 2949<html> 2950<head><title>Chaosreader External Image Report</title></head> 2951<body bgcolor="white" textcolor="black"> 2952<font size=+3>Chaosreader External Image Report</font><br> 2953<font size=+1>Created at: $the_date, Type: $TYPE</font><p> 2954<font size=+2>Images</font><br> 2955<table border=2> 2956END_HTML 2957 for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) { 2958 $html_line = $ExtImage{HTML}[$html_index]{info}; 2959 $html_links = $ExtImage{HTML}[$html_index]{links}; 2960 next unless defined $html_links; 2961 print FILE "$html_line $html_links </td></tr>\n"; 2962 } 2963 print FILE <<END_HTML; 2964</table><p> 2965</body> 2966</html> 2967END_HTML 2968 2969 2970 ###################### 2971 # --- getpost.html --- 2972 2973 # 2974 # Create HTML GETPOST Index file to show HTTP GETs and POSTs 2975 # 2976 open(FILE,">getpost.html") || die "ERROR25: creating index: $!\n"; 2977 print FILE <<END_HTML; 2978<html> 2979<head><title>Chaosreader GET/POST Report</title></head> 2980<body bgcolor="white" textcolor="black"> 2981<font size=+3>Chaosreader GET/POST Report</font><br> 2982<font size=+1>Created at: $the_date, Type: $TYPE</font><p> 2983<font size=+2>HTTP GETs and POSTs</font><br> 2984<table border=2> 2985END_HTML 2986 for ($html_index=0; $html_index <= $#{$GETPOST{HTML}}; $html_index++) { 2987 $html_line = $GETPOST{HTML}[$html_index]{info}; 2988 $html_links = $GETPOST{HTML}[$html_index]{query}; 2989 next unless defined $html_links; 2990 print FILE "$html_line $html_links </td></tr>\n"; 2991 } 2992 print FILE <<END_HTML; 2993</table><p> 2994</body> 2995</html> 2996END_HTML 2997 2998 } 2999} 3000 3001 3002 3003# Create_Index_Master - Create the HTML and text master index files. This 3004# reads @Master and creates the files on disk. 3005# 3006sub Create_Index_Master { 3007 3008 my ($start,$end,$dir,$file,$index,$duration); 3009 3010 if ($Arg{output_index}) { 3011 3012 # 3013 # Create most recent link 3014 # 3015 3016 $dir = $Master[$#Master]{dir}; 3017 $recentname = "most_recent_index"; 3018 unlink("$recentname"); 3019 # don't die on symlink error, it's not essential 3020 symlink("$dir","$recentname"); 3021 3022 # 3023 # Create HTML Index file containing all reports 3024 # 3025 open(FILE,">index.html") || die "ERROR26: creating index: $!\n"; 3026 print FILE <<END_HTML; 3027<html> 3028<head><title>Chaosreader Master Index</title></head> 3029<body bgcolor="white" textcolor="black" vlink="blue"> 3030<font size=+3>Chaosreader Master Index</font><br> 3031<font size=+1>Created at: $the_date, Type: $TYPE</font><p> 3032<a href="$recentname/index.html"><font color="red"> 3033<b>Most Recent Report</b></font></a> 3034 - Click here for the most recent index, and click reload for updates.<p> 3035<font size=+2>Chaosreader Reports</font><br> 3036<table border=2> 3037END_HTML 3038 for ($index=0; $index <= $#Master; $index++) { 3039 $start = $Master[$index]{starttime}; 3040 $end = $Master[$index]{endtime}; 3041 $dir = $Master[$index]{dir}; 3042 $file = $Master[$index]{file}; 3043 $size = $Master[$index]{size}; 3044 $duration = $Master[$index]{duration}; 3045 $html_line = "<tr><td><i>". ($index+1) . "</i></td>" . 3046 "<td><b>$start</b></td><td><b>$end</b></td>\n" . 3047 "<td>$duration s</td>" . "<td><font color=\"green\"> " . 3048 "$size bytes</font></td>" . 3049 "<td><a href=\"$dir/index.html\">$dir/$file</a></td></tr>\n"; 3050 print FILE "$html_line </td></tr>\n"; 3051 } 3052 print FILE <<END_HTML; 3053</table><p> 3054<font size=+2>IP Count</font><br> 3055<table border=2> 3056END_HTML 3057 foreach $IP (sort {$CountMaster{IP}{$b} <=> $CountMaster{IP}{$a}} 3058 keys %{$CountMaster{IP}}) { 3059 print FILE "<tr><td>$IP</td><td>$CountMaster{IP}{$IP}" . 3060 "</td></tr>\n"; 3061 } 3062 print FILE <<END_HTML; 3063</table><p> 3064<font size=+2>TCP Port Count</font><br> 3065<table border=2> 3066END_HTML 3067 foreach $port (sort {$CountMaster{TCPport}{$b} <=> 3068 $CountMaster{TCPport}{$a}} keys %{$CountMaster{TCPport}}) { 3069 $port_text = $Services_TCP{$port} || $port || "0"; 3070 print FILE "<tr><td>$port_text</td><td>" . 3071 "$CountMaster{TCPport}{$port}</td></tr>\n"; 3072 } 3073 print FILE <<END_HTML; 3074</table><p> 3075<font size=+2>UDP Port Count</font><br> 3076<table border=2> 3077END_HTML 3078 foreach $port (sort {$CountMaster{UDPport}{$b} <=> 3079 $CountMaster{UDPport}{$a}} keys %{$CountMaster{UDPport}}) { 3080 $port_text = $Services_UDP{$port} || $port || "0"; 3081 print FILE "<tr><td>$port_text</td><td>" . 3082 "$CountMaster{UDPport}{$port}</td></tr>\n"; 3083 } 3084 print FILE <<END_HTML; 3085</table><p> 3086<font size=+2>IP Protocol Count</font><br> 3087<table border=2> 3088END_HTML 3089 foreach $protocol (sort {$CountMaster{IPprotocol}{$b} <=> 3090 $CountMaster{IPprotocol}{$a}} keys %{$CountMaster{IPprotocol}}) { 3091 $protocol_text = $IP_Protocols{$protocol}; 3092 print FILE "<tr><td>$protocol_text</td><td>" . 3093 "$CountMaster{IPprotocol}{$protocol}</td></tr>\n"; 3094 } 3095 print FILE <<END_HTML; 3096</table><p> 3097<font size=+2>Ethernet Type Count</font><br> 3098<table border=2> 3099END_HTML 3100 foreach $type (sort {$CountMaster{EtherType}{$b} <=> 3101 $CountMaster{EtherType}{$a}} keys %{$CountMaster{EtherType}}) { 3102 print FILE "<tr><td>$type</td><td>" . 3103 "$CountMaster{EtherType}{$type}</td></tr>\n"; 3104 } 3105 print FILE <<END_HTML; 3106</table> 3107</body> 3108</html> 3109END_HTML 3110 3111 # 3112 # Create Text index file 3113 # 3114 open(FILE,">index.text") || die "ERROR27: creating index: $!\n"; 3115 print FILE "Master Indexes\nCreated at: $the_date, Type: $TYPE\n\n"; 3116 for ($index=0; $index <= $#Master; $index++) { 3117 $start = $Master[$index]{starttime}; 3118 $end = $Master[$index]{endtime}; 3119 $dir = $Master[$index]{dir}; 3120 $file = $Master[$index]{file}; 3121 $size = $Master[$index]{size}; 3122 $duration = $Master[$index]{duration}; 3123 printf FILE "%-25s %3s s %8s b %s\n",$start,$duration, 3124 $size,"$dir/index.text"; 3125 } 3126 close FILE; 3127 3128 3129 # 3130 # Create index.file for redos 3131 # 3132 open(FILE,">index.file") || die "ERROR28: creating index: $!\n"; 3133 for ($index=0; $index <= $#Master; $index++) { 3134 $dir = $Master[$index]{dir}; 3135 $file = $Master[$index]{file}; 3136 $start = $Master[$index]{starttime}; 3137 $end = $Master[$index]{endtime}; 3138 $duration = $Master[$index]{duration}; 3139 print FILE "$dir\t$file\t$duration\t$start\t$end\n"; 3140 } 3141 close FILE; 3142 } 3143} 3144 3145 3146# JL: Print a line for the HTTPlog 3147# 3148sub Print_Log_Line { 3149 my $number = shift; 3150 my $time = shift; 3151 my $duration = shift; 3152 my $src = shift; 3153 my $dest = shift; 3154 my $result = shift; 3155 my $status = shift; 3156 my $size = shift; 3157 my $method = shift; 3158 my $site = shift; 3159 my $type = shift; 3160 3161 if ($Arg{httplog_html}) { 3162 sprintf("<pre><a href=\"index.html#%d\">%d</a>" . 3163 " %9d.%03d %6d " . 3164 "%-15s %-15s %s/%03d %d %s %s %s %s%s/%s %s</pre><br/>\n", 3165 $number,$number, 3166 int($time),(($time - int($time))*1000),($duration*1000), 3167 $src,$dest,$result,$status,$size, 3168 $method,$site,"-","NONE","","-",$type); 3169 } else { 3170 sprintf("%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s\n", 3171 int($time),(($time - int($time))*1000),($duration*1000), 3172 $src,$result,$status,$size, 3173 $method,$site,"-","NONE","","-",$type); 3174 } 3175} 3176 3177# JL: Print a line for the new text HTTPlog 3178# 3179sub Print_TxtLog_Line { 3180 my $number = shift; 3181 my $time = shift; 3182 my $referer = shift; 3183 my $cookie = shift; 3184 my $setcookie = shift; 3185 my $method = shift; 3186 my $site = shift; 3187 3188 ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime($time); 3189 $referer = "Referer: " . $referer if $referer ne ""; 3190 $cookie = "Cookie sent." if $cookie ne ""; 3191 $setcookie = "Sets cookie." if $setcookie ne ""; 3192 sprintf("%-4s %02d:%02d:%02d %s %s %s %s %s\n", 3193 $number,$hour,$minute,$second, 3194 $method,$site,$referer,$cookie,$setcookie); 3195} 3196 3197 3198# Create_Log_Files - create log files such as the HTTP log. 3199# 3200sub Create_Log_Files { 3201 #BDG some memory debug 3202 #system("pmap -x $$"); 3203 3204 # 3205 # Create httplog file 3206 # JL: Don't use hardcoded filename 3207 # 3208 open(FILE,">$Arg{httplog_name}") || die "ERROR29: creating HTTP log: $!\n"; 3209 foreach $time (sort { $a <=> $b }(keys (%{$HTTPlog{time}}))) { 3210 print FILE $HTTPlog{time}{$time}; 3211 } 3212 3213 close FILE; 3214 3215 open(FILE,">$Arg{httplog_txt}") || die "ERROR29: creating HTTP text log: $!\n"; 3216 3217 foreach $time (sort { $a <=> $b }(keys (%{$HTTPtxtlog{time}}))) { 3218 print FILE $HTTPtxtlog{time}{$time}; 3219 } 3220 3221 close FILE; 3222} 3223 3224 3225 3226# File_Type - return file extension for given data, else "data". 3227# 3228sub File_Type { 3229 my $data = $_[0]; 3230 my $type = ""; 3231 3232 if ( $http_data eq "" ) { 3233 return "empty"; 3234 } 3235 if ( length($http_data) < 8 ) { 3236 return "small"; 3237 } 3238 if ($http_header =~ /Content-Encoding: deflate/ ){ 3239 return "deflate"; 3240 } 3241 3242 if ($data =~ /^GIF8[7-9]/) { $type = "gif"; } 3243 elsif ($data =~ /^\377.....(JPEG|JFIF)/) { $type = "jpeg"; } 3244 elsif ($data =~ /^.PNG/) { $type = "png"; } # JL 3245 elsif ($data =~ /^PK\003\004/) { $type = "zip"; } 3246 elsif ($data =~ /^\%PDF/) { $type = "pdf"; } 3247 elsif ($data =~ /^\037\213/) { $type = "gz"; } 3248 elsif ($data =~ /^BZh/) { $type = "bz2"; } 3249 elsif ($data =~ /^\177ELF/) { $type = "elf"; } 3250 elsif ($data =~ /^\%!/) { $type = "ps"; } 3251 elsif ($data =~ /<html>/i) { $type = "html"; } 3252 elsif ($data =~ /<?xml/i) { $type = "xml"; } # JL 3253 else { $type = "data"; } 3254 3255 return $type; 3256} 3257 3258 3259# Is_Image - returns true if extension is for an image. 3260# 3261sub Is_Image { 3262 my $ext = shift; 3263 3264 # JL: Use MIME types. 3265 return ($ext_types{$ext} eq "image"); 3266} 3267 3268 3269# Desex_HTML - Removes HTML tags ("<" and ">") from data, so that it no 3270# longer interferes when printed as HTML. 3271# 3272sub Desex_HTML { 3273 ### Input 3274 my $data = shift; 3275 3276 ### Process 3277 # remove "<" and ">"s 3278 $data =~ s/</</g; 3279 $data =~ s/>/>/g; 3280 3281 ### Return 3282 return $data; 3283} 3284 3285 3286 3287# Process_BothHTML - Process the HTML 2-way session. Remove binary junk 3288# that dosen't render well in a browser. 3289# 3290sub Process_BothHTML { 3291 ### Input 3292 my $type = shift; 3293 my $session_id = shift; 3294 my $plain = shift; 3295 my $wrapped = ""; 3296 my $index = 0; 3297 my $counter = 0; 3298 my $intag = 0; 3299 my ($char,$data); 3300 3301 if ($type eq "TCP") { 3302 $data = $TCP{id}{$session_id}{BothHTML}; 3303 } elsif ($type eq "UDP") { 3304 $data = $UDP{id}{$session_id}{BothHTML}; 3305 } elsif ($type eq "ICMP") { 3306 $data = $ICMP{time}{$session_id}{BothHTML}; 3307 } 3308 3309 ### Process (order dependant) 3310 $data =~ s/font color="red"> \0</font color="red"></g; 3311 $data =~ tr/\040-\176\n\r\f/./c; # max 376, was 245 3312 if (defined $plain) { 3313 # This is a plain style of line wrap 3314 $data =~ s/([^\n\f<>]{$WRAP})/$&\n/g; 3315 } else { 3316 # This is a fancy line wrap, a green ">" starts the wrapped lines 3317 $data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/font>/g; 3318 } 3319 3320 ### Save 3321 if ($type eq "TCP") { 3322 $TCP{id}{$session_id}{BothHTML} = $data; 3323 } elsif ($type eq "UDP") { 3324 $UDP{id}{$session_id}{BothHTML} = $data; 3325 } elsif ($type eq "ICMP") { 3326 $ICMP{time}{$session_id}{BothHTML} = $data; 3327 } 3328 3329} 3330 3331# Process_This_HTML - Process the HTML 2-way session. Remove binary junk 3332# that dosen't render well in a browser. 3333# 3334sub Process_This_HTML { 3335 ### Input 3336 my $data = shift; 3337 my $plain = shift; 3338 my $wrapped = ""; 3339 my $index = 0; 3340 my $counter = 0; 3341 my $intag = 0; 3342 my ($char); 3343 3344 ### Process (order dependant) 3345 $data =~ s/font color="red"> \0</font color="red"></g; 3346 $data =~ tr/\040-\176\n\r\f/./c; # max 376, was 245 3347 if (defined $plain) { 3348 # This is a plain style of line wrap 3349 $data =~ s/([^\n\f<>]{$WRAP})/$&\n/g; 3350 } else { 3351 # This is a fancy line wrap, a green ">" starts the wrapped lines 3352 $data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/font>/g; 3353 } 3354 3355 return $data; 3356} 3357 3358 3359# Process_Hex - Create the coloured HTML 2-way hex dump, and a text dump. 3360# Uses data stored to data structure %Hex. 3361sub Process_Hex { 3362 ### Input 3363 my $type = shift; 3364 my $session_id = shift; 3365 my $offset = 0; 3366 my (@Bytes,$byte,$colour,$from_server,$hexhtml,$hextext,$html,$pos,$text,$view,$view2,$viewhtml,$viewtext); 3367 3368 3369 ### Process 3370 foreach $from_server_AND_data (@{$Hex{$type}{$session_id}}) { 3371 ($from_server, $data) = @{$from_server_AND_data}; 3372 $colour = $from_server ? "blue" : "red"; 3373 $pos = 1 unless defined $pos; 3374 $hexhtml .= "<font color=\"$colour\">"; 3375 $viewhtml .= "<font color=\"$colour\">"; 3376 @Bytes = unpack("C*", $data); 3377 foreach $byte (@Bytes) { 3378 $view = chr($byte); 3379 $view =~ tr/\040-\176/./c; 3380 $view2 = $view; 3381 $view2 =~ s/</</g; 3382 $view2 =~ s/>/>/g; 3383 $viewhtml .= $view2; 3384 $viewtext .= $view; 3385 $hexhtml .= sprintf("%2.2x",$byte); 3386 $hextext .= sprintf("%2.2x",$byte); 3387 $pos++; 3388 if ($pos > 16) { 3389 ### Save text version 3390 $text .= sprintf("%6.08x",$offset) . " $hextext $viewtext\n"; 3391 3392 ### Save HTML version 3393 $hexhtml .= "</font>"; 3394 $viewhtml .= "</font>"; 3395 $html .= '<font color="green">' . sprintf("%6.08x",$offset) . "</font> $hexhtml $viewhtml\n"; 3396 3397 $pos = 1; 3398 $offset += 16; 3399 $hexhtml = "<font color=\"$colour\">"; 3400 $viewhtml = "<font color=\"$colour\">"; 3401 $hextext = $viewtext = ""; 3402 } 3403 if ( ($pos != 1) && (($pos %2) == 1) ) { 3404 $hexhtml .= " "; 3405 $hextext .= " "; 3406 } 3407 } 3408 $hexhtml .= "</font>"; 3409 $viewhtml .= "</font>"; 3410 } 3411 3412 return unless defined $pos; 3413 return ($text, $html) if $pos == 1; 3414 3415 $short = 39 - length($hextext); 3416 $hexhtml .= " " x $short; 3417 $hextext .= " " x $short; 3418 3419 ### Save text version 3420 $text .= sprintf("%6.08x",$offset) . " $hextext $viewtext\n"; 3421 3422 ### Save HTML version 3423 $html .= '<font color="green">' . sprintf("%6.08x",$offset) . "</font> $hexhtml $viewhtml\n"; 3424 3425 return ($text, $html) 3426} 3427 3428 3429# Generate_X11_HTML - fetch the text from an X11 session and save 3430# as bidirectional 2-way coloured HTML. 3431# 3432# Todo: check if a text or keypress event can be split during 3433# transmission and add code similar to X11 replay to handle this. 3434# 3435sub Generate_X11_HTML { 3436 my ($filename,$data,$copy,$xcode,$xbyte,$xlength,$xrest,$d, 3437 $xlv,$xvalue,$pad,$y,$yold,$chars,$colour,$session_data, 3438 $service_name,$colourold,$store,$keytype,$gotsome); 3439 my @Times; 3440 3441 $session_data = ""; 3442 3443 ### Input 3444 my $session_id = shift; 3445 $data = ""; 3446 $service_name = "X11"; 3447 3448 ### Processing 3449 my $session_text = $session_id; 3450 $session_text =~ s/,/ <-> /; 3451 3452 ### Fetch raw data 3453 $xserver = &TCP_Follow_RawA($session_id); 3454 3455 # 3456 # Determine endian of this transfer. 3457 # 3458 ($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver); 3459 # 3460 # Create aliases for "n" and "N". 3461 # 3462 if ($xvalue < 256) { 3463 $n = "n"; $N = "N"; 3464 } else { 3465 $n = "v"; $N = "V"; 3466 } 3467 # 3468 # Determine keymap style - see &Set_X11_KeyCodes() 3469 # 3470 if ($xserver =~ 3471 /q...Q.*w...W.*e...E.*r...R.*t...T.*y...Y.*u...U.*i...I.*o...O.*p/) { 3472 $keytype = "linux"; 3473 } else { 3474 $keytype = "sun"; 3475 } 3476 3477 # 3478 # Fetch data from both directions, sorting on timestamps 3479 # 3480 @Times = sort{$a <=> $b} (keys %{$TCP{id}{$session_id}{time}}); 3481 3482 # 3483 # --- Main Loop --- 3484 # 3485 # (this needs to be a for loop!) 3486 for ($i=0; $i <= $#Times; $i++) { 3487 $time = $Times[$i]; 3488 3489 ### Fetch X11 data and direction as a colour 3490 if (defined $TCP{id}{$session_id}{time}{$time}{dir}) { 3491 $copy = $TCP{id}{$session_id}{time}{$time}{data}; 3492 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { 3493 $colour = "red"; 3494 } else { 3495 $colour = "blue"; 3496 } 3497 } 3498 3499 $xrest = $copy; 3500 # 3501 # Process through X11 codes 3502 # 3503 while (length($xrest) > 0) { 3504 ### Fetch xcode and other values 3505 ($xcode,$xbyte,$xlength,$xrest) = unpack("CC${n}a*",$xrest); 3506 $chars = ""; 3507 3508 # 3509 # Fetch code values from $xrest, and trim 3510 # $xrest. For most requests, the value length 3511 # is a field (bytes 3,4) except for XErrors 3512 # (code 0) where the total length is always 32. 3513 # 3514 if ($xcode == 0) { 3515 $xlv = 28; 3516 } else { 3517 $xlv = ($xlength - 1) * 4; 3518 $xlv = -$xlv if $xlv < 0; 3519 } 3520 3521 ### Fetch values for this xcode 3522 ($xvalue,$xrest) = unpack("a${xlv}a*",$xrest); 3523 3524 $store = 0; 3525 3526 # 3527 # Process a draw text event (76, 77) 3528 # 3529 if (($colour eq "blue") && (($xcode == 76)||($xcode == 77))) { 3530 # Check if this is a xImageText16Req 3531 if ($xcode == 77) { $xbyte *= 2; } 3532 3533 ($pad,$y,$chars) = unpack("a10${n}a$xbyte",$xvalue); 3534 if ($yold != $y) { $chars = "\n$chars"; } 3535 $chars =~ s/\0//g; 3536 3537 $store = 1; 3538 $yold = $y; 3539 } 3540 3541 # 3542 # Process a key pressed event (2) 3543 # 3544 if (($colour eq "red") && ($xcode = "2")) { 3545 ($pad,$caps,$pad) = unpack("a24${n}a*",$xvalue); 3546 3547 # 3548 # Translate the X11 KeyCode to the actual char 3549 # (try "xmodmap -pke") 3550 # 3551 $chars = $KeyCode{$keytype}{$caps}{$xbyte}; 3552 3553 ### Don't keep red \n's for neatness (keep blue ones) 3554 unless ($chars eq "\n") { 3555 $store = 1; 3556 } 3557 } 3558 3559 # 3560 # Process a text scroll event (by using 62 - copy area) 3561 # 3562 if (($colour eq "blue") && ($xcode == 62)) { 3563 $chars = "\n"; 3564 $store = 1; 3565 } 3566 3567 ### Store data 3568 if ($store) { 3569 if ($colour ne $colourold) { 3570 $session_data .= 3571 "</font><font color=\"$colour\">$chars"; 3572 } else { 3573 $session_data .= $chars; 3574 } 3575 $colourold = $colour; 3576 } 3577 } 3578 } 3579 3580 $TCP{id}{$session_id}{BothHTML} = $session_data; 3581} 3582 3583 3584# Save_Both_HTML - Save bidirectional (coloured) data into a html file. 3585# 3586sub Save_Both_HTML { 3587 my ($filename); 3588 3589 ### Input 3590 my $type = shift; 3591 my $session_id = shift; 3592 my $number = shift; 3593 my $service_name = shift; 3594 my $session_text = shift; 3595 my $numtext = sprintf("%04d",$number); 3596 my ($base,$raw); 3597 3598 $session_text = $session_id unless defined $session_text; 3599 3600 ### Processing 3601 $session_text =~ s/,/ <-> /; 3602 3603 ### Checks 3604 $ext = ""; 3605 $session_data = ""; 3606 if ($type eq "TCP") { 3607 $base = "session"; 3608 # 3609 # Note, the following is similar code for TCP, UDP and ICMP. 3610 # However UDP and ICMP use a simple strategy to store and fetch 3611 # the processed HTML; whereas TCP uses a complex yet memory 3612 # efficient strategy. This is intentional - the way TCP has 3613 # been stored has been tuned to reduce memory usage, as TCP has 3614 # the bulk of the data (and the bulk of the memory problem). This 3615 # has not been necessary with UDP and ICMP (yet). 3616 # 3617 if ($TCP{id}{$session_id}{BothHTML} ne "") { 3618 # 3619 # If the BothHTML report has already been calculated, fetch 3620 # 3621 $session_data = $TCP{id}{$session_id}{BothHTML}; 3622 } else { 3623 # 3624 # Generate a BothHTML report by following packets by time 3625 # 3626 foreach $time (sort {$a <=> $b} 3627 (keys (%{$TCP{id}{$session_id}{time}}))) { 3628 $raw = $TCP{id}{$session_id}{time}{$time}{data}; 3629 $raw = &Desex_HTML($raw); 3630 next unless length($raw); 3631 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { 3632 $session_data .= "<font color=\"blue\">$raw</font>"; 3633 } else { 3634 $session_data .= "<font color=\"red\">$raw</font>"; 3635 } 3636 } 3637 $session_data = &Process_This_HTML($session_data); 3638 $base = "session"; 3639 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3640 } 3641 3642 } elsif ($type eq "UDP") { 3643 $base = "stream"; 3644 $session_data = $UDP{id}{$session_id}{BothHTML}; 3645 if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3646 } elsif ($type eq "ICMP") { 3647 $base = "icmp"; 3648 $session_data = $ICMP{time}{$session_id}{BothHTML}; 3649 if ($ICMP{time}{$session_id}{Partial}) { $ext = ".partial"; } 3650 } else { 3651 $base = "are_belong_to_us"; 3652 } 3653 3654 ### Do nothing if there is no data ("26" is mostly due to colour tags) 3655 return unless ((defined $session_data)&&(length($session_data) > 26)); 3656 3657 ### Output 3658 $filename = "${base}_${numtext}.${service_name}${ext}.html"; 3659 open (OUT,">$filename") || die "ERROR30: file create, $filename: $!\n"; 3660 binmode(OUT); 3661 print OUT "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" . 3662 "<BODY bgcolor=\"white\">\n" . 3663 "<H1>$service_name: $session_text</H1>\n" . 3664 "<H2>File $Arg{infile}, Session $number</H2>\n" . 3665 "<PRE WRAP=\"virtual\">\n" . 3666 $session_data . "</PRE>\n</BODY>\n</HTML>\n"; 3667 close OUT; 3668 3669 ### Global Vars 3670 my $length = length($session_data); 3671 $Index{HTML}[$number] .= "<li><a href=\"$filename\">as_html</a></li>\n"; 3672 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 3673 '"' , " $filename","",$length); 3674} 3675 3676 3677# Save_Hex_HTML - Save bidirectional (coloured) hex data into a html file. 3678# 3679sub Save_Hex_HTML { 3680 my ($filename); 3681 3682 ### Input 3683 my $type = shift; 3684 my $session_id = shift; 3685 my $number = shift; 3686 my $service_name = shift; 3687 my $session_text = shift; 3688 my $session_data = shift; 3689 my $numtext = sprintf("%04d",$number); 3690 my ($base); 3691 3692 $session_text = $session_id unless defined $session_text; 3693 $session_data = "" unless defined $session_data; 3694 3695 3696 ### Processing 3697 $session_text =~ s/,/ <-> /; 3698 3699 ### Checks 3700 $ext = ""; 3701 if ($type eq "TCP") { 3702 $base = "session"; 3703 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3704 } elsif ($type eq "UDP") { 3705 $base = "stream"; 3706 if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3707 } elsif ($type eq "ICMP") { 3708 $base = "icmp"; 3709 if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3710 } 3711 3712 ### Output 3713 $filename = "${base}_${numtext}.${service_name}${ext}.hex.html"; 3714 open (OUT,">$filename") || die "ERROR31: file create, $filename: $!\n"; 3715 binmode(OUT); 3716 print OUT "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" . 3717 "<BODY bgcolor=\"white\">\n" . 3718 "<H1>$service_name: $session_text</H1>\n" . 3719 "<H2>File $Arg{infile}, Session $number</H2>\n" . 3720 "<PRE WRAP=\"virtual\">\n" . 3721 $session_data . "</PRE>\n</BODY>\n</HTML>\n"; 3722 close OUT; 3723 3724 ### Global Vars 3725 my $length = length($session_data); 3726 $Index{HTML}[$number] .= "<li>"; 3727 $Index{HTML}[$number] .= "<a href=\"$filename\">hex</a></li>\n"; 3728 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 3729 '"' , " $filename","",$length); 3730} 3731 3732 3733# Save_Hex_Text - Save bidirectional hex data into a text file. 3734# 3735sub Save_Hex_Text { 3736 my ($filename); 3737 3738 ### Input 3739 my $type = shift; 3740 my $session_id = shift; 3741 my $number = shift; 3742 my $service_name = shift; 3743 my $session_text = shift; 3744 my $session_data = shift; 3745 my $numtext = sprintf("%04d",$number); 3746 my ($base); 3747 3748 $session_text = $session_id unless defined $session_text; 3749 $session_data = "" unless defined $session_data; 3750 3751 ### Processing 3752 $session_text =~ s/,/ <-> /; 3753 3754 ### Checks 3755 $ext = ""; 3756 if ($type eq "TCP") { 3757 $base = "session"; 3758 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3759 } elsif ($type eq "UDP") { 3760 $base = "stream"; 3761 if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3762 } elsif ($type eq "ICMP") { 3763 $base = "icmp"; 3764 if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3765 } 3766 3767 ### Output 3768 $filename = "${base}_${numtext}.${service_name}${ext}.hex.text"; 3769 open (OUT,">$filename") || die "ERROR32: file create, $filename: $!\n"; 3770 binmode(OUT); 3771 print OUT "$service_name: $session_text\n" . 3772 "File $Arg{infile}, Session $number\n\n$session_data\n"; 3773 close OUT; 3774 3775 ### Global Vars 3776 my $length = length($session_data); 3777 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 3778 '"' , " $filename","",$length); 3779} 3780 3781 3782# Save_FTP_File - Save files from an active FTP session. 3783# 3784sub Save_FTP_File { 3785 my ($filename,$ftp_data,$length); 3786 my $session_id = shift; 3787 my $number = shift; 3788 my $numtext = sprintf("%04d",$number); 3789 my $service_name = "ftp-data"; 3790 3791 ### Input 3792 $ftp_data = &TCP_Follow_RawB($session_id); 3793 if (! defined $ftp_data) { 3794 $ftp_data = &TCP_Follow_RawA($session_id); 3795 } 3796 3797 ### Checks 3798 $ftp_type = &File_Type($ftp_data); 3799 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 3800 else { $ext = ""; } 3801 3802 ### Output 3803 $filename = "session_${numtext}.part_01.$service_name${ext}.$ftp_type"; 3804 open (OUT,">$filename") || die "ERROR33: file create, $filename: $!\n"; 3805 binmode(OUT); # for backward OSs 3806 print OUT $ftp_data; 3807 close OUT; 3808 3809 ### Global Vars 3810 $length = length($ftp_data); 3811 $Index{HTML}[$number] .= 3812 "<li><a href=\"$filename\">$filename</a> $length bytes</li>\n"; 3813 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 3814 '"' , " $filename","",$length); 3815 if (&Is_Image($ftp_type)) { 3816 $Image{HTML}[$number]{links} .= 3817 "<img src=\"$filename\"> "; 3818 $Image{notempty} = 1; 3819 } 3820} 3821 3822# NOTE On Replays 3823# 3824# The essence of these is to playback the client/server data so that 3825# the original session can be replayed. There are two styles, 3826# 3827# Text Replays. These playback the text component to the application 3828# data to the screen. These usally work well. The actual text data is not 3829# cleaned up in any way, so to preserve escape sequences necessary to 3830# redisplay in the original style. Eg, telnet. 3831# 3832# GUI Replays, or Server/Client Replays. These often use TCP/IP to send 3833# the data back to the server or client to playback the session. These 3834# are less robust, mainly becuase negotiation can occur slightly differently 3835# causing nothing to be displayed. There is code here to redo the 3836# negotiation - but it is very difficult for this to be 100% robust. 3837# The main reasons the GUI replays fail are colour depth mismatch 3838# and dropped packets. Eg, X11. 3839# 3840# Both styles print the binary data within single quotes ' '. This 3841# creates perl programs that can't be "cat" (use cat -vet), or edited 3842# in vi (use vim) due to the raw binary data. A neater style would be to 3843# translate the binary data into octal or hex text streams, eg 3844# 'print "\015\012\087\012"'... Currently this is not used, as it would 3845# roughly increase the file size by a factor of 4. However plopping 3846# data in the middle of perl programs creates problems of it's own 3847# (see the unusual seds). At some point I may opt for the easier, 3848# although lengthier, method. 3849 3850 3851# Save_Session_Replay - Save a replay program for this session. eg, telnet. 3852# 3853sub Save_Session_Replay { 3854 my ($filename,$duration,$time); 3855 my $session_id = shift; 3856 my $number = shift; 3857 my $service_name = shift; 3858 my $numtext = sprintf("%04d",$number); 3859 3860 ### Output 3861 $filename = "session_${numtext}.${service_name}.replay"; 3862 $duration = ($TCP{id}{$session_id}{EndTime} - 3863 $TCP{id}{$session_id}{StartTime}); 3864 $duration = sprintf("%.0f",$duration); 3865 open (REPLAY,">$filename") || 3866 die "ERROR34: creating $filename $!\n"; 3867 binmode(REPLAY); # for backward OSs 3868 3869 # 3870 # Create a perl program, that when run itself will print out 3871 # the contents of the server 1-way stream, with pauses based on 3872 # the packet arrival times (replay the session in realtime). 3873 # 3874 print REPLAY "#!$PERL\n"; 3875 print REPLAY <<'END'; 3876# 3877# This is a telnet/login replay program. It will replay a session using 3878# the timestamps from the packet log. 3879# 3880# USAGE: run the script as normal. You can provide a factor as an 3881# argument, eg "2" to run twice as fast, or "0.5" to run 3882# at half time. eg, 3883# ./session_0002.telnet.replay 2 3884# 3885# Auto generated by Chaosreader. 3886# 3887$| = 1; 3888$factor = $ARGV[0] || 1; 3889sub ms { 3890 $ms = shift; 3891 $ms = $ms / $factor; 3892 select(undef, undef, undef, $ms); 3893} 3894END 3895 3896 # 3897 # Sort the data on the timestamps, calculating timestamp differences 3898 # to record in the replay program. 3899 # 3900 @Times = (); 3901 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 3902 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { 3903 push(@Times,$time) 3904 } 3905 } 3906 @Times = sort { $a <=> $b } @Times; 3907 3908 for ($i=0; $i <= $#Times; $i++) { # required 3909 3910 ### Calculate time diff if possible 3911 if ($i == $#Times) { 3912 $timediff = 0; 3913 } else { 3914 $timediff = $Times[$i+1] - $Times[$i]; 3915 if ($timediff < 0) { $timediff = 0; } 3916 } 3917 $time = $Times[$i]; 3918 3919 ### Fetch data from mem 3920 $data = $TCP{id}{$session_id}{time}{$time}{data}; 3921 3922 # 3923 # Clean the data a little (order important) 3924 # 3925 $data =~ s/\\/\\\\/g; # backslash the backslashes 3926 $data =~ s/'/\\'/g; # backslash single quotes 3927 3928 # 3929 # Now output the data in the replay program 3930 # 3931 print REPLAY "print '" . $data . "';\n"; 3932 3933 # 3934 # This causes the replay program to pause 3935 # 3936 print REPLAY "ms($timediff);\n"; 3937 } 3938 close REPLAY; 3939 3940 ### Better make it executable 3941 chmod (0755, "$filename"); 3942 3943 ### Global Vars 3944 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 3945 "</a> $duration seconds</li>\n"; 3946 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 3947 '"' , " $filename","",$duration); 3948} 3949 3950 3951# Save_Session_textSSH_files - Save a replay program to display the SSH 3952# session in a text format, a html form of this, and a key delay 3953# data file. 3954# 3955# The program "sshkeydata" will take the key delay data file and estimate 3956# the original typed commands. (It also needs a key delay data file 3957# from a plaintext session such as telnet, which is generated by the 3958# Save_Session_Keydata subroutine). 3959# 3960# This has been designed with SSH ver 2 in mind. 3961# 3962sub Save_Session_textSSH_files { 3963 my ($filename1,$filename2,$filename3,$duration,$time,$data,$length, 3964 $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2, 3965 $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize,$datah, 3966 $data00); 3967 my $session_id = shift; 3968 my $number = shift; 3969 my $service_name = shift; 3970 my $session_text = shift; 3971 my $numtext = sprintf("%04d",$number); 3972 my $delay = ""; # a text list of key delays 3973 my $html = ""; # a html form of output 3974 my $bytes = 0; # data bytes of the connection 3975 my $minsize; # The min client packet size 3976 my $state; 3977 3978 $duration = ($TCP{id}{$session_id}{EndTime} - 3979 $TCP{id}{$session_id}{StartTime}); 3980 $duration2 = sprintf("%.2f",$duration); 3981 $duration = sprintf("%.0f",$duration); 3982 3983 ### Output 3984 $filename1 = "session_${numtext}.text${service_name}.replay"; 3985 open (REPLAY,">$filename1") || 3986 die "ERROR35: creating $filename1 $!\n"; 3987 binmode(REPLAY); # for backward OSs 3988 3989 # 3990 # Create a perl program that replays details of the original 3991 # SSH session. We print the direction of traffic and size, 3992 # paused using the original delays. 3993 # 3994 print REPLAY "#!$PERL\n"; 3995 print REPLAY <<'END'; 3996# 3997# This is a text SSH replay program. It will replay details of the 3998# original SSH session using timestamps from the packet capture log. 3999# 4000# USAGE: run the script as normal. You can provide a factor as an 4001# argument, eg "2" to run twice as fast, or "0.5" to run 4002# at half time. eg, 4003# ./session_0002.textSSH.replay 2 4004# 4005# Auto generated by Chaosreader. 4006# 4007$| = 1; 4008$factor = $ARGV[0] || 1; 4009sub ms { 4010 $ms = shift; 4011 $ms = $ms / $factor; 4012 select(undef, undef, undef, $ms); 4013} 4014print <<'SUBEND'; 4015SSH text analysis replay 4016------------------------ 4017"*" is client traffic (including keystrokes), "." is the return text. 4018A number is a multiple of the previous char, eg ".32" is 32 return chars. 4019 4020SUBEND 4021END 4022 4023 # 4024 # Sort the data on the timestamps, calculating timestamp differences 4025 # to record in the replay program. 4026 # 4027 @Times = (); 4028 %PacketSize = (); 4029 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 4030 if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) { 4031 next; 4032 } 4033 push(@Times,$time); 4034 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { 4035 ### Frequency count sent sizes 4036 $data = $TCP{id}{$session_id}{time}{$time}{data}; 4037 $length = length($data); 4038 $PacketSize{$length}++ if $length < 100; 4039 } 4040 } 4041 @Times = sort { $a <=> $b } @Times; 4042 $outtime = $Times[0]; 4043 $outsize = 0; 4044 4045 # 4046 # Determine the client min size - this is the minimum length of 4047 # a data packet, eg a keystroke. 4048 # 4049 foreach $length (sort {$PacketSize{$b} <=> $PacketSize{$a}} 4050 (keys(%PacketSize))) { 4051 $minsize = $length; 4052 last; 4053 } 4054 4055 # The very first packet 4056 $data00 = $TCP{id}{$session_id}{time}{$Times[0]}{data}; 4057 4058 ### Process data 4059 for ($i=0; $i <= $#Times; $i++) { # required 4060 4061 ### Calculate time diff if possible 4062 $time0 = $Times[$i]; 4063 $time1 = $Times[$i+1]; 4064 $time2 = $Times[$i+2]; 4065 $time3 = $Times[$i+3]; 4066 if ($i == $#Times) { 4067 $timediff1 = 0; 4068 $timediff2 = 0; 4069 } else { 4070 $timediff1 = $time1 - $time0; 4071 $timediff2 = $time2 - $time0; 4072 if ($timediff1 < 0) { $timediff1 = 0; } 4073 } 4074 4075 ### Fetch data from mem, "0" is this packet... 4076 $data0 = $TCP{id}{$session_id}{time}{$time0}{data}; 4077 $data1 = $TCP{id}{$session_id}{time}{$time1}{data}; 4078 $data2 = $TCP{id}{$session_id}{time}{$time2}{data}; 4079 $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir}; 4080 $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir}; 4081 $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir}; 4082 $dir3 = $TCP{id}{$session_id}{time}{$time3}{dir}; 4083 $length0 = length($data0); 4084 $length1 = length($data1); 4085 $length2 = length($data2); 4086 4087 # working variables 4088 $bytes += $length0; 4089 $length = $length0; 4090 $data = $data0; 4091 4092 ################## 4093 # Process Data 4094 # 4095 # This is designed for a command line SSH session and 4096 # the calculations are based on many assumptions. 4097 # 4098 # For example: if the client sends a small packet (which 4099 # we'll assume is a keystroke) and the server responds 4100 # with large packets (beyond merely echoing the keystroke), 4101 # then we can assume that this keystroke was the enter key, 4102 # and the large response was the output of the command. 4103 # 4104 # There are two states - keystrokes and output text. 4105 # 4106 # The follow code works well most of the time, and provides 4107 # meaningful results for non command line sessions. 4108 # 4109 4110 # 4111 # --- Server to Client --- 4112 # 4113 if ($dir0 eq "A") { 4114 if ($i > 3 || $data00 !~ /^ssh/i) { 4115 # a "." represents an encrypted server to client packet 4116 $data = "."; 4117 $html .= '<font color="blue">' . $data; 4118 } else { 4119 ### Process initial plaintext negotiation 4120 4121 # first we clean up the data, 4122 $data =~ tr/\040-\176/./c; 4123 $data =~ s/\\/\\\\/g; 4124 $data =~ s/'/\\'/g; 4125 $data .= "\n"; 4126 $hdata = $data; 4127 $hdata = &Desex_HTML($hdata); 4128 4129 # This is a fancy line wrap, adds a green ">" 4130 $hdata =~ 4131 s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/font>/g; 4132 $html .= '<font color="blue">' . $hdata; 4133 } 4134 4135 if ($state eq "output") { 4136 if ($length0 > $minsize && $i > 3) { 4137 # This prints the length in the replay files 4138 # as a number following the symbol, 4139 # eg ".60" would mean a "." with length 60. 4140 # length actually means size beyond minsize. 4141 $length -= $minsize; 4142 $data .= "$length"; 4143 $html .= "$length"; 4144 $outsize += $length; 4145 } 4146 4147 ### Data -> Keystrokes 4148 if ($dir1 eq "B" && $length1 == $minsize) { 4149 # Process the transition from command output back 4150 # to keystrokes. 4151 $data .= "\n"; 4152 $html .= "\n"; 4153 $delay .= "s $outsize\n"; 4154 $delay .= sprintf("t %.6f\n",$time0 - $outtime); 4155 $delay .= " \n"; # command delimiter 4156 $outsize = 0; 4157 $outtime = $time0; 4158 $state = "key"; 4159 } 4160 } 4161 $html .= '</font>'; 4162 } 4163 4164 # 4165 # --- Client to Server --- 4166 # 4167 else { 4168 if ($i == 1) { 4169 # PuTTY appears to have an unusual way to send keystrokes 4170 # to the server, that differs to OpenSSH and Sun's SSH. 4171 # Remember if this is a PuTTY session. 4172 $sshtype = "putty" if $data =~ /PuTTY/; 4173 } 4174 4175 ### Keystroke 4176 if ($sshtype eq "") { 4177 # If the client is sending a minsize packet and the server 4178 # then responds, we assume this is a keystroke. 4179 if ($length0 == $minsize && $dir1 eq "A") { 4180 $delay .= "k \n"; 4181 } 4182 } elsif ($sshtype eq "putty") { 4183 # if the client is sending a minsize packet, followed by 4184 # another packet, then a reply packet, and then a server 4185 # response; we assume that this is a keystroke. 4186 # (This processes PuTTY's doubled keystrokes). 4187 if ($length0 == $minsize && $dir1 eq "B" && $dir2 eq "A") { 4188 $delay .= "k \n"; 4189 } 4190 } 4191 4192 ### Process initial plaintext negotiation 4193 if ($i > 3 || $data00 !~ /^ssh/i) { 4194 # a "*" represents an encrypted client to server packet 4195 $data = "*"; 4196 $html .= '<font color="red">' . $data; 4197 } else { 4198 ### Process initial plaintext negotiation 4199 4200 # first we clean up the data, 4201 $data =~ tr/\040-\176/*/c; 4202 $data =~ s/\\/\\\\/g; 4203 $data =~ s/'/\\'/g; 4204 $data .= "\n"; 4205 $hdata = $data; 4206 $hdata = &Desex_HTML($hdata); 4207 4208 # This is a fancy line wrap, adds a green ">" 4209 $hdata =~ 4210 s/([^\n\f<>]{$WRAP})/$&\n<font color="green">><\/font>/g; 4211 $html .= '<font color="red">' . $hdata; 4212 } 4213 4214 ### Keystroke -> Keystroke delay 4215 if ($sshtype eq "") { 4216 if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && 4217 $length2 == $minsize) { 4218 # If this is a keystroke packet, and the next packet 4219 # is a response, and then another keystroke packet 4220 # is sent; then measure the keystroke delay. 4221 $timediff2 = $time2 - $time0; 4222 $delay .= sprintf("d %.6f\n",$timediff2); 4223 $outsize = 0; 4224 $outtime = $time0; 4225 } 4226 } elsif ($sshtype eq "putty") { 4227 if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && 4228 $length2 == $minsize && $dir3 eq "B") { 4229 # This is the same idea as the above, but processes 4230 # PuTTY's doubled keystrokes. 4231 $timediff2 = $time2 - $time0; 4232 $delay .= sprintf("d %.6f\n",$timediff2); 4233 $outsize = 0; 4234 $outtime = $time0; 4235 } 4236 } 4237 4238 if ($length0 > $minsize && $i > 3) { 4239 # 4240 # This prints the length in the replay files 4241 # as a number following the symbol, 4242 # eg ".60" would mean a "." with length 60. 4243 # length actually means size beyond minsize. 4244 $length -= $minsize; 4245 $data .= "$length"; 4246 $html .= "$length"; 4247 } 4248 $html .= '</font>'; 4249 4250 ### Keystrokes -> Data 4251 if ( ($length0 == $minsize && 4252 (($length1 + $length2) > ($minsize * 2))) || 4253 ($dir1 eq "A" && $dir2 eq "A") ) { 4254 $data .= "\n"; 4255 $html .= "\n"; 4256 # 4257 # "r" describes the response packet. This value 4258 # may or may not be meaningful depending on the 4259 # SSH software. 4260 if ($length1 > $minsize) { 4261 $delay .= "r 1\n"; 4262 $delay .= sprintf("p %.6f\n",$timediff1); 4263 } else { 4264 $delay .= "r 2\n"; 4265 $delay .= sprintf("p %.6f\n",$timediff2); 4266 } 4267 $state = "output"; 4268 } 4269 } 4270 4271 ### Now output the data in the replay program 4272 print REPLAY "print '" . $data . "';\n"; 4273 4274 ### This causes the replay program to pause 4275 print REPLAY "ms($timediff1);\n"; 4276 } 4277 $duration = 0.01 if $duration == 0; # avoid divide by 0, 4278 if ( $duration > 0 ) { 4279 $speed = sprintf("%.2f",$bytes / (1024 * $duration)); 4280 } else { 4281 $speed = "unknown"; 4282 } 4283 print REPLAY "print \"\n\n" . 4284 "Summary: $duration2 seconds, $bytes bytes, $speed Kb/sec\\n\";"; 4285 close REPLAY; 4286 4287 ### Better make it executable 4288 chmod (0755, "$filename1"); 4289 4290 # 4291 # HTML version of the replay script 4292 # 4293 $filename2 = "session_${numtext}.text${service_name}.html"; 4294 open (HTML,">$filename2") || 4295 die "ERROR36: Can't write to file, $filename2 $!\n"; 4296 $html = "<html><head><title>SSH text analysis</title></head>\n" . 4297 "<body bgcolor=\"white\">" . 4298 "<H1>$service_name: $session_text</H1>\n" . 4299 "<H2>File $Arg{infile}, Session $number</H2>\n" . 4300 "<h3>$duration2 seconds, $bytes bytes, $speed Kb/sec</h3>\n" . 4301 '"*" is client traffic (including ' . 4302 'keystrokes), "." is the return ' . 4303 'text.<br>A number is a multiple of the previous char, eg ".32" ' . 4304 'is 32 return chars.<br>' . 4305 "\n<b><pre>$html</pre></b>\n</body>\n</html>\n"; 4306 print HTML $html; 4307 close HTML; 4308 4309 # 4310 # Text Database of time delays between possible keystrokes 4311 # 4312 $filename3 = "session_${numtext}.text${service_name}.keydata"; 4313 open (DELAY,">$filename3") || 4314 die "ERROR37: Can't write keydata file: $filename3 $!\n"; 4315 $delay = "$delay \n"; 4316 print DELAY $delay; 4317 close DELAY; 4318 4319 # 4320 # Update Global Vars to remember new filenames 4321 # 4322 $Index{HTML}[$number] .= "<li><a href=\"$filename1\">$filename1" . 4323 "</a> $duration seconds</li>\n"; 4324 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 4325 '"' , " $filename1","",$duration); 4326 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" . 4327 "</a> </li>\n"; 4328 $length = length($html); 4329 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 4330 '"' , " $filename2","",$length); 4331 $Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" . 4332 "</a> </li>\n"; 4333 $length = length($delay); 4334 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 4335 '"' , " $filename3","",$length); 4336} 4337 4338 4339# Save_Session_Keydata - Save a key delay data file to assist SSH analysis. 4340# 4341# This code is intentionally designed to be similar to the SSH processing 4342# code, so that both their outputs can be compared. As a standalone 4343# subroutine this wouldn't make too much sense; instead bear in mind that 4344# I'd like the processing to mimic how SSH was processed. That way we 4345# run this on plenty of known text (telnet) and become familiar with 4346# exactly what will happen for the unknown text (SSH). 4347# 4348sub Save_Session_Keydata { 4349 my ($filename1,$filename2,$filename3,$duration,$time,$data,$length, 4350 $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2, 4351 $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize); 4352 my $session_id = shift; 4353 my $number = shift; 4354 my $service_name = shift; 4355 my $session_text = shift; 4356 my $numtext = sprintf("%04d",$number); 4357 my $delay = ""; # a text list of key delays 4358 my $minsize; # The min client packet size 4359 my $state = "key"; 4360 4361 ### Sort the data by timestamps 4362 @Times = (); 4363 %PacketSize = (); 4364 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 4365 if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) { 4366 next; 4367 } 4368 push(@Times,$time); 4369 } 4370 @Times = sort { $a <=> $b } @Times; 4371 $outtime = $Times[0]; 4372 $outsize = 0; 4373 $minsize = 1; # known for telnet 4374 4375 ### Process data 4376 for ($i=0; $i <= $#Times; $i++) { # required 4377 4378 ### Calculate time diff if possible 4379 $time0 = $Times[$i]; 4380 $time1 = $Times[$i+1]; 4381 $time2 = $Times[$i+2]; 4382 if ($i == $#Times) { 4383 $timediff1 = 0; 4384 $timediff2 = 0; 4385 } else { 4386 $timediff1 = $time1 - $time0; 4387 $timediff2 = $time2 - $time0; 4388 if ($timediff1 < 0) { $timediff1 = 0; } 4389 } 4390 4391 ### Fetch data from mem, "0" is this packet... 4392 $data0 = $TCP{id}{$session_id}{time}{$time0}{data}; 4393 $data1 = $TCP{id}{$session_id}{time}{$time1}{data}; 4394 $data2 = $TCP{id}{$session_id}{time}{$time2}{data}; 4395 $data0 = "\n" if $data0 eq "\r\n"; 4396 $data1 = "\n" if $data1 eq "\r\n"; 4397 $data2 = "\n" if $data2 eq "\r\n"; 4398 $data0 = "\n" if $data0 =~ /\r./; 4399 $data1 = "\n" if $data1 =~ /\r./; 4400 $data2 = "\n" if $data2 =~ /\r./; 4401 $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir}; 4402 $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir}; 4403 $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir}; 4404 $length0 = length($data0); 4405 $length1 = length($data1); 4406 $length2 = length($data2); 4407 4408 $length = $length0; 4409 $data = $data0; 4410 4411 # 4412 # Process Data 4413 # 4414 if ($dir0 eq "A") { 4415 if ($state eq "output") { 4416 if ($length0 > $minsize) { 4417 $length -= $minsize; 4418 $outsize += $length; 4419 } 4420 4421 ### Data -> Keystrokes 4422 if ($dir1 eq "B" && $length1 == $minsize) { 4423 $delay .= "s $outsize\n"; 4424 $delay .= sprintf("t %.6f\n",$time0 - $outtime); 4425 $delay .= " \n"; 4426 $outsize = 0; 4427 $outtime = $time0; 4428 $state = "key"; 4429 } 4430 } 4431 } else { 4432 ### Keystroke 4433 if ($length0 == $minsize) { 4434 if ($data0 eq "\n") { 4435 $delay .= "k \\n\n"; 4436 } else { 4437 $delay .= "k $data0\n"; 4438 } 4439 } 4440 ### Keystroke -> Keystroke delay 4441 if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" && 4442 $length2 == $minsize) { 4443 $timediff2 = $time2 - $time0; 4444 $delay .= sprintf("d %.6f\n",$timediff2); 4445 $outsize = 0; 4446 $outtime = $time0; 4447 } 4448 4449 if ($length0 > $minsize) { 4450 $length -= $minsize; 4451 } 4452 4453 ### Keystrokes -> Data 4454 if ( ($length0 == $minsize && 4455 (($length1 + $length2) > ($minsize * 2))) || 4456 ($dir1 eq "A" && $dir2 eq "A") ) { 4457 if ($length1 > $minsize) { 4458 $delay .= "r 1\n"; 4459 $delay .= sprintf("p %.6f\n",$timediff1); 4460 } else { 4461 $delay .= "r 2\n"; 4462 $delay .= sprintf("p %.6f\n",$timediff2); 4463 } 4464 $state = "output"; 4465 } 4466 } 4467 } 4468 4469 # 4470 # Text Database of time delays between possible keystrokes 4471 # 4472 $filename3 = "session_${numtext}.${service_name}.keydata"; 4473 open (DELAY,">$filename3") || 4474 die "ERROR38: A pink jelly hits you. You die. $filename3 $!\n"; 4475 print DELAY "$delay \n"; 4476 close DELAY; 4477 4478 # 4479 # Update Global Vars to remember new filenames 4480 # 4481 $Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" . 4482 "</a> </li>\n"; 4483 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s\n", 4484 '"' , " $filename3","",""); 4485} 4486 4487 4488# Save_Stream_Replay - Save a replay program for this stream. eg, dns. 4489# 4490sub Save_Stream_Replay { 4491 my ($filename,$duration); 4492 my $session_id = shift; 4493 my $number = shift; 4494 my $service_name = shift; 4495 my $numtext = sprintf("%04d",$number); 4496 4497 ### Output 4498 $filename = "stream_${numtext}.${service_name}.replay"; 4499 $duration = ($UDP{id}{$session_id}{EndTime} - 4500 $UDP{id}{$session_id}{StartTime}); 4501 $duration = sprintf("%.0f",$duration); 4502 open (REPLAY,">$filename") || 4503 die "ERROR39: creating $filename $!\n"; 4504 binmode(REPLAY); # for backward OSs 4505 4506 # 4507 # Create a perl program, that when run itself will print out 4508 # the contents of the server 1-way stream, with pauses based on 4509 # the packet arrival times (replay the stream in realtime). 4510 # 4511 print REPLAY "#!$PERL\n"; 4512 print REPLAY <<'END'; 4513# 4514# This is a UDP replay program. It will replay a stream using 4515# the timestamps from the packet log. 4516# 4517# USAGE: run the script as normal. You can provide a factor as an 4518# argument, eg "2" to run twice as fast, or "0.5" to run 4519# at half time. eg, 4520# ./stream_0002.telnet.replay 2 4521# 4522# Auto generated by Chaosreader. 4523# 4524$| = 1; 4525$factor = $ARGV[0] || 1; 4526sub ms { 4527 $ms = shift; 4528 $ms = $ms / $factor; 4529 select(undef, undef, undef, $ms); 4530} 4531END 4532 4533 # 4534 # Sort the data on the timestamps, calculating timestamp differences 4535 # to record in the replay program. 4536 # 4537 @Times = keys (%{$UDP{id}{$session_id}{time}}); 4538 @Times = sort { $a <=> $b } @Times; 4539 4540 for ($i=0; $i <= $#Times; $i++) { # required 4541 4542 ### Calculate time diff if possible 4543 if ($i == $#Times) { 4544 $timediff = 0; 4545 } else { 4546 $timediff = $Times[$i+1] - $Times[$i]; 4547 if ($timediff < 0) { $timediff = 0; } 4548 } 4549 $time = $Times[$i]; 4550 4551 ### Fetch data from mem 4552 $data = $UDP{id}{$session_id}{time}{$time}; 4553 delete $UDP{id}{$session_id}{time}{$time}; 4554 4555 # 4556 # Clean the data a little (order important) 4557 # 4558 $data =~ s/\\/\\\\/g; # backslash the backslashes 4559 $data =~ s/'/\\'/g; # backslash single quotes 4560 4561 # 4562 # Now output the data in the replay program 4563 # 4564 print REPLAY "print '" . $data . "';\n"; 4565 4566 # 4567 # This causes the replay program to pause 4568 # 4569 print REPLAY "ms($timediff);\n"; 4570 } 4571 close REPLAY; 4572 4573 ### Better make it executable 4574 chmod (0755, "$filename"); 4575 4576 ### Global Vars 4577 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 4578 "</a> $duration seconds</li>\n"; 4579 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 4580 '"' , " $filename","",$duration); 4581} 4582 4583 4584# Save_Session_XReplay - Save a replay program for this session. eg, X11. 4585# This processes far more of the X11 protocol than I was hoping. 4586# (xscope and ethereal were used to analyse X11). 4587# 4588sub Save_Session_XReplay { 4589 my $session_id = shift; 4590 my $number = shift; 4591 my $service_name = shift; 4592 my $numtext = sprintf("%04d",$number); 4593 my ($filename,$duration,$xcode,$xres_old,$xrest,$xwnum,$xdiff, 4594 $xlength,$xmsb,$xstart,$xjunk,$xvalue,$readnow,$data,$newdata, 4595 $n,$N,$chars,$y,$timediff,$texttimediff,$checkdepth,$filename2, 4596 $x11type); 4597 my @xWords; 4598 4599 ### Initials 4600 $xmsb = ""; 4601 $readnow = 0; 4602 $xres_old = -1; 4603 $checkdepth = 0; 4604 4605 # 4606 # Output - Main X11 replay program 4607 # 4608 $filename = "session_${numtext}.${service_name}.replay"; 4609 $duration = ($TCP{id}{$session_id}{EndTime} - 4610 $TCP{id}{$session_id}{StartTime}); 4611 $duration = sprintf("%.0f",$duration); 4612 open (REPLAY,">$filename") || 4613 die "ERROR40: creating $filename $!\n"; 4614 binmode(REPLAY); # for backward OSs 4615 4616 # 4617 # Output - Text (keystroke replay) 4618 # 4619 $filename2 = "session_${numtext}.text${service_name}.replay"; 4620 open (REPLAY2,">$filename2") || 4621 die "ERROR41: creating $filename2 $!\n"; 4622 binmode(REPLAY2); # for backward OSs 4623 4624 4625 # --- textX11 --- 4626 # 4627 # Create a perl program, that when run itself will print out 4628 # the contents of the server 1-way stream, with pauses based on 4629 # the packet arrival times (replay the session in realtime). 4630 # 4631 print REPLAY2 "#!$PERL\n"; 4632 print REPLAY2 <<'END'; 4633# 4634# This is an X11 text replay program. It will replay keystrokes and text 4635# of an X11 session using the timestamps from the packet log. 4636# 4637# USAGE: run the script as normal. You can provide a factor as an 4638# argument, eg "2" to run twice as fast, or "0.5" to run 4639# at half time. eg, 4640# ./session_0002.textX11.replay 2 4641# 4642# Auto generated by Chaosreader. 4643# 4644$| = 1; 4645$factor = $ARGV[0] || 1; 4646sub ms { 4647 $ms = shift; 4648 $ms = $ms / $factor; 4649 select(undef, undef, undef, $ms); 4650} 4651END 4652 4653 4654 # --- X11 --- 4655 # 4656 # Create a perl program, that when run itself will print out 4657 # the contents of the server 1-way stream, with pauses based on 4658 # the packet arrival times (replay the session in realtime). 4659 # 4660 print REPLAY "#!$PERL\n"; 4661 print REPLAY <<'END'; 4662# 4663# This is a X11 replay program. It will replay a session using 4664# the timestamps from the packet log, and transpose the X11 protocol so 4665# that it can be redisplayed. You must have captured from the start 4666# of the connection for this to work. 4667# 4668# USAGE: ./session_0001.X11.replay [-d destination host] [-p port] factor 4669# 4670# just run the script as normal. You can provide a factor as an 4671# argument, eg "2" to run twice as fast, or "0.5" to run 4672# at half time. eg, 4673# ./session_0002.X11.replay 2 4674# a different host and port can be specified if needed. eg, 4675# ./session_0002.X11.replay -d 192.168.1.5 -p 6001 4676# 4677# PROBLEMS: you may need to authorise this connection to the X11 server 4678# before it works. You could run "xhost +hostname" beforehand. 4679# The playback needs to have captured the start of the connection. 4680# Check you support the same colour depth as the playback. And check 4681# the playback file simply isn't too big! (more than 500 Kb is 4682# currently problematic). 4683# 4684# 4685# Auto generated by Chaosreader. 4686# 4687 4688use IO::Socket; 4689use Getopt::Std; 4690 4691if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); } 4692 4693# Try fetching values from $DISPLAY 4694($hostdef,$portdef) = $ENV{DISPLAY} =~ /([^:]*):(\d*)/; 4695$hostdef = "127.0.0.1" if $hostdef eq ""; 4696$portdef += 6000; 4697 4698# Command line options take preference 4699&getopts('d:p:'); 4700if (defined $opt_d) { $host = $opt_d; } else { $host = $hostdef; } 4701if (defined $opt_p) { $port = $opt_p; } else { $port = $portdef; } 4702$factor = $ARGV[0] || 1; 4703$DEBUG = 0; 4704$| = 1; 4705 4706print "Chaosreader X11 Replay (experimental)\n\n"; 4707print "Connecting to $host:$port\n"; 4708print "(problems? try running \"xhost +hostname\" first).\n\n"; 4709 4710 4711# --- Open Socket --- 4712# 4713$remote = IO::Socket::INET->new( Proto => "tcp", 4714 PeerAddr => $host, 4715 PeerPort => $port, 4716 ); 4717unless ($remote) { die "ERROR42: Can't connect to X11 daemon on $host:$port"; } 4718$remote->autoflush(1); 4719 4720 4721# --- Subroutines --- 4722# 4723 4724# ms - sleeps for specified milliseconds 4725# 4726sub ms { 4727 $ms = shift; 4728 $ms = $ms / $factor; 4729 select(undef, undef, undef, $ms); 4730} 4731# help - print help 4732# 4733sub help { 4734 open (MYSELF,"$0") || die "ERROR43: I can't see myself: $!\n"; 4735 @Myself = <MYSELF>; 4736 close MYSELF; 4737 ### Print comment from top of code 4738 foreach $line (@Myself) { 4739 last if $line !~ /^#/; 4740 next if $line =~ m:^#!/usr/bin/perl:; 4741 $line =~ s/^#/ /; 4742 print $line; 4743 } 4744 print "\n"; 4745 exit(0); 4746} 4747# R - recalculates and prints a resourse setting 4748# The single character subroutine name saves on file space below. 4749# 4750sub R { 4751 #$offset = shift; 4752 #$new = $res + $offset; 4753 my $rid = shift; 4754 my $new; 4755 4756 # final checks 4757 $diff = $rid - $ridbaseold; 4758 $diff = -$diff if $diff < 0; 4759 if ((($rid < $ridbaseold) && ($rid < 8196)) || ($diff > 8196)) { 4760 if ($msb) { return pack('N',$rid); } 4761 else { return pack('V',$rid); } 4762 } 4763 4764 $new = $rid & $ridmaskold; 4765 $new = $new | $ridbase; 4766 if ($msb) { return pack('N',$new); } 4767 else { return pack('V',$new); } 4768} 4769# D - prints the new Drawable, usually the rootid. 4770# 4771sub D { 4772 my $rid = shift; 4773 4774 # final checks 4775 if ($rid >= $ridbaseold) { 4776 # return mapped resource id 4777 return R($rid); 4778 } 4779 # return rootid 4780 if ($msb) { return pack('N',$rootid); } 4781 else { return pack('V',$rootid); } 4782} 4783# C - prints the new Colour map. 4784# 4785sub C { 4786 my $rid = shift; 4787 4788 # final checks 4789 if ($rid >= $ridbaseold) { 4790 # return mapped resource id 4791 return R($rid); 4792 } 4793 # return colour map 4794 if ($msb) { return pack('N',$colour); } 4795 else { return pack('V',$colour); } 4796} 4797# M - Returns a generic mapped id. Can be rootid, colour, or resource. 4798# These are used in Xcodes involving a mask. 4799# 4800sub M { 4801 my $rid = shift; 4802 4803 # final checks 4804 if ($rid >= $ridbaseold) { 4805 # return mapped resource id 4806 return R($rid); 4807 } 4808 # return rootid map 4809 if ($rid == $rootidold) { 4810 if ($msb) { return pack('N',$rootid); } 4811 else { return pack('V',$rootid); } 4812 } 4813 # return colour map 4814 if ($rid == $colourold) { 4815 if ($msb) { return pack('N',$colour); } 4816 else { return pack('V',$colour); } 4817 } 4818 # return other 4819 if ($msb) { return pack('N',$rid); } 4820 else { return pack('V',$rid); } 4821} 4822# P - Check depth pixels, print warning if there is a mismatch. 4823# 4824sub P { 4825 my $depth = shift; 4826 if (! defined $Depth{$depth}) { 4827 print "\nWARNING: requested depth $depth may not be ". 4828 "supported by the server?\n"; 4829 } 4830} 4831# debug - print out a value 4832# 4833sub debug { 4834 my $word = shift; 4835 my $num = shift; 4836 my $pack = pack("N",$num); 4837 print "$word: $num ", 4838 sprintf("%2.2x%2.2x%2.2x%2.2x\n",unpack("C*",$pack)); 4839} 4840 4841 4842# --- MAIN --- 4843# 4844print "Sending X11 traffic:"; 4845END 4846 ### Fetch raw data 4847 $xserver = &TCP_Follow_RawA($session_id); 4848 4849 # 4850 # Determine endian of this transfer. Reading the 4851 # second short on MSB gives 11, and on LSB 2816 4852 # (at least in testing). We split the difference 4853 # on 256 (is case there is a little variation). 4854 # 4855 ($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver); 4856 # 4857 # Create aliases for "n" and "N" so I can think 4858 # in big endian. 4859 # 4860 if ($xvalue < 256) { 4861 $xmsb = 1; 4862 $n = "n"; 4863 $N = "N"; 4864 } else { 4865 $xmsb = 0; 4866 $n = "v"; 4867 $N = "V"; 4868 } 4869 my ($success,$major,$minor,$length,$release,$ridbase, 4870 $ridmask,$mbsize,$vendor,$reqmax,$roots,$formats,$ibo, 4871 $bbo,$bslu,$bslp,$keymin,$keymax,$pad,$rest) = 4872 unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$xserver); 4873 4874 ($x11type,$rest) = unpack("a${vendor}a*",$rest); 4875 $pad = ((4 - ($vendor % 4)) % 4); 4876 ($junk,$rest) = unpack("a${pad}a*",$rest); 4877 4878 foreach $i (1..$formats) { 4879 ($junk,$rest) = unpack("a8a*",$rest); 4880 } 4881 ($rootid,$colour,$junk) = unpack("$N${N}a*",$rest); 4882 4883 # 4884 # Sort the data on the timestamps, calculating timestamp differences 4885 # to record in the replay program. 4886 # 4887 @Times = (); 4888 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 4889 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { 4890 push(@Times,$time) 4891 } 4892 } 4893 @Times = sort { $a <=> $b } @Times; 4894 4895 # 4896 # --- Main Loop --- 4897 # 4898 # (this needs to be a for loop!) 4899 for ($i=0; $i <= $#Times; $i++) { 4900 4901 ### Calculate time diff if possible 4902 if ($i == $#Times) { 4903 $timediff = 0; 4904 } else { 4905 $timediff = $Times[$i+1] - $Times[$i]; 4906 # just in case, 4907 if ($timediff < 0) { $timediff = 0; } 4908 } 4909 $time = $Times[$i]; 4910 $texttimediff += $timediff; 4911 4912 ### Fetch data from mem 4913 $data = $TCP{id}{$session_id}{time}{$time}{data}; 4914 4915 ### If initial request was fetched, 4916 if ($readnow == 0) { 4917 ### Populate $xstart with initial request 4918 $xstart .= $data; 4919 4920 # 4921 # This triggers the replay program to ask the X11 4922 # server for the connection data - which 4923 # needs to be processed so that various 4924 # resource offsets can be used later on. 4925 # 4926 if (length($xstart) >= 12) { 4927 $readnow = 1; 4928 } 4929 4930 } else { 4931 # 4932 # Change resource offsets 4933 # (reads $data and writes to $data) 4934 # 4935 $xrest = $data; 4936 $data = ""; # output stream of data & subs 4937 4938 # 4939 # Process through X11 codes 4940 # 4941 while (length($xrest) > 0) { 4942 ($xcode,$xbyte,$xlength,$xrest) = 4943 unpack("CC${n}a*",$xrest); 4944 4945 ### Add xcode to output stream $data 4946 $d = pack("CC${n}",$xcode,$xbyte,$xlength); 4947 # the unusual seds 4948 $d =~ s/\\/\\\\/g; 4949 $d =~ s/'/\\'/g; 4950 $d =~ s/\015\012/'."\\015\\012".'/gs; 4951 $data .= $d; 4952 4953 # 4954 # Fetch code values from $xrest, and trim 4955 # $xrest. For most requests, the value length 4956 # is a field (bytes 3,4) except for XErrors 4957 # (code 0) where the total length is always 32. 4958 # 4959 if ($xcode == 0) { 4960 $xlv = 28; 4961 } else { 4962 $xlv = ($xlength - 1) * 4; 4963 $xlv = -$xlv if $xlv < 0; 4964 } 4965 while (length($xrest) < $xlv) { 4966 # some more magic 4967 $i++; 4968 last if ($i > $#Times); 4969 4970 $next = $Times[$i]; 4971 4972 ### Fetch data from mem 4973 $xrest .= 4974 $TCP{id}{$session_id}{time}{$next}{data}; 4975 } 4976 4977 ($xvalue,$xrest) = unpack("a${xlv}a*",$xrest); 4978 4979 #$format = "%2.2x%2.2x " x ($xlv/2); 4980 #printf("X$xcode: $xbyte,$xlength $format\n", 4981 # unpack("C*",$xvalue)); ### Debug 4982 4983 $xwnum = 0; 4984 @xWords = unpack("${N}*",$xvalue); 4985 4986 # 4987 # If this is a text event, save the text to the 4988 # textX11 replay program. 4989 # 4990 if (($xcode == 76) || ($xcode == 77)) { 4991 4992 # Check if this is a xImageText16Req 4993 if ($xcode == 77) { $xbyte *= 2; } 4994 4995 ($pad,$y,$chars) = 4996 unpack("a10${n}a$xbyte",$xvalue); 4997 if ($yold != $y) { $chars = "\n$chars"; } 4998 4999 ### Clean the data a little (order important) 5000 $chars =~ s/\\/\\\\/g; 5001 $chars =~ s/'/\\'/g; 5002 $chars =~ s/\0//g; 5003 5004 ### Now output the data in the replay program 5005 print REPLAY2 "print '" . $chars . "';\n"; 5006 5007 ### This causes the replay program to pause 5008 print REPLAY2 "ms($texttimediff);\n" 5009 unless $texttimediff < 0.002; 5010 5011 $yold = $y; 5012 $texttimediff = 0; 5013 } 5014 # 5015 # Process a text scroll event (by using 62 - copy area) 5016 # 5017 if ($xcode == 62) { 5018 print REPLAY2 "print \"\\n\";\n"; 5019 $chars = "\n"; 5020 } 5021 5022 5023 # 5024 # If this is a create window event, check the depth. 5025 # 5026 if (($xcode == 1) && ($checkdepth == 0)) { 5027 $data .= "',P($xbyte),'"; 5028 $checkdepth = 1; 5029 } 5030 5031 # 5032 # Print the X11 data with embedded subroutines 5033 # to transpose the resource IDs. 5034 # 5035 foreach $xw (@xWords) { 5036 $xwnum++; 5037 if ($X11_Codes[$xcode][$xwnum] == 1) { 5038 $data .= "',R($xw),'"; 5039 #print "XCODER: $xcode, $xwnum\n"; 5040 } elsif ($X11_Codes[$xcode][$xwnum] == 2) { 5041 $data .= "',D($xw),'"; 5042 #print "XCODED: $xcode, $xwnum\n"; 5043 } elsif ($X11_Codes[$xcode][$xwnum] == 3) { 5044 $data .= "',C($xw),'"; 5045 #print "XCODEC: $xcode, $xwnum\n"; 5046 } elsif ($X11_Codes[$xcode][$xwnum] == 4) { 5047 $data .= "',M($xw),'"; 5048 #print "XCODEM: $xcode, $xwnum\n"; 5049 } else { 5050 $d = pack("$N",$xw); 5051 $d =~ s/\\/\\\\/g; 5052 $d =~ s/'/\\'/g; 5053 $d =~ s/\015\012/'."\\015\\012".'/gs; 5054 $data .= $d; 5055 } 5056 } 5057 } 5058 } 5059 5060 # 5061 # Now output the data in the replay program 5062 # 5063 print REPLAY "print '.';\n"; 5064 print REPLAY "print \$remote '" . $data . "';\n"; 5065 5066 if ($readnow == 1) { 5067 $readnow = 2; 5068 print REPLAY "\$msb = $xmsb;\n"; 5069 print REPLAY "\$ridbaseold = $ridbase;\n"; 5070 print REPLAY "\$ridmaskold = $ridmask;\n"; 5071 print REPLAY "\$rootidold = $rootid;\n"; 5072 print REPLAY "\$colourold = $colour;\n"; 5073 # 5074 # The following code implements the client to 5075 # server connection - we need to read the 5076 # resource and window IDs which are necessary 5077 # when transposing the replay traffic to 5078 # these new values. 5079 # 5080 print REPLAY <<'END'; 5081if ($msb) { 5082 $n = "n"; 5083 $N = "N"; 5084} else { 5085 $n = "v"; 5086 $N = "V"; 5087} 5088 5089 5090read($remote,$in,40); # (xConnSetup) 5091($success,$major,$minor,$length,$release,$ridbase,$ridmask,$mbsize,$vendor, 5092$reqmax,$roots,$formats,$ibo,$bbo,$bslu,$bslp,$keymin,$keymax,$pad) = 5093unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$in); 5094 5095read($remote,$in,$vendor); 5096print "\nX11 Server Type: $in\n"; 5097read($remote,$in,((4 - ($vendor % 4)) % 4)); 5098 5099foreach $i (1..$formats) { 5100 read($remote,$in,8); # (xPixmapFormat) 5101 ($depth,$junk) = unpack("Ca*",$in); 5102 $Depth{$depth} = 1; 5103 next if $depth == 1; 5104 print "X11 server supports $depth bit resolution\n"; 5105} 5106read($remote,$in,8); # (xWindowRoot) 5107($rootid,$colour,$junk) = unpack("$N$N",$in) unless defined $rootid; 5108 5109if ($DEBUG) { 5110 debug("Resource ID new: ",$ridbase); 5111 debug("Resource ID old: ",$ridbaseold); 5112 debug("Root ID new: ",$rootid); 5113 debug("Root ID old: ",$rootidold); 5114 debug("Colour map new: ",$colour); 5115 debug("Colour map old: ",$colourold); 5116} 5117END 5118 } 5119 5120 # 5121 # This causes the replay program to pause 5122 # 5123 print REPLAY "ms($timediff);\n" 5124 unless $timediff < 0.002; # (efficiency). 5125 } 5126 print REPLAY "print \"\n\";\n"; 5127 print REPLAY "close \$remote;\n"; 5128 close REPLAY; 5129 5130 ### Better make it executable 5131 chmod (0755, "$filename"); 5132 5133 close REPLAY2; 5134 ### Better make it executable 5135 chmod (0755, "$filename2"); 5136 5137 ### Global Vars 5138 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 5139 "</a> $duration seconds</li>\n"; 5140 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" . 5141 "</a> $duration seconds</li>\n"; 5142 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 5143 '"' , " $filename","",$duration); 5144 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 5145 '"' , " $filename2","",$duration); 5146} 5147 5148 5149 5150# Save_Session_VNCReplay_andHTML - Save a replay program for this session. 5151# This creates a program that is used in conjunction with vncviewer. 5152# It also saves the HTML version (it would have been redundant to 5153# create a seperate subroutine for that). 5154# 5155sub Save_Session_VNCReplay_andHTML { 5156 my $session_id = shift; 5157 my $number = shift; 5158 my $service_name = shift; 5159 my $session_text = shift; 5160 my $numtext = sprintf("%04d",$number); 5161 my ($filename,$filename2,$filename3,$duration,$code,$rest,$extra, 5162 $length,$start,$junk,$down,$value,$data,$oldtimediff,$printed,$chars, 5163 $char,$timediff,$checkdepth,$html); 5164 my @xWords; 5165 5166 $oldtimediff = 0; 5167 $printed = 0; 5168 $html = ""; 5169 5170 5171 # 5172 # Output - Text (keystroke replay) 5173 # 5174 $filename2 = "session_${numtext}.text${service_name}.replay"; 5175 open (REPLAY2,">$filename2") || 5176 die "ERROR44: creating $filename2 $!\n"; 5177 binmode(REPLAY2); # for backward OSs 5178 5179 # 5180 # --- textVNC --- 5181 # 5182 # Create a perl program, that when run itself will print out 5183 # the contents of the client 1-way stream, with pauses based on 5184 # the packet arrival times (replay the session in realtime). 5185 # 5186 print REPLAY2 "#!$PERL\n"; 5187 print REPLAY2 <<'END'; 5188# 5189# This is an VNC text replay program. It will replay keystrokes from 5190# a VNC session using the timestamps from the packet log. 5191# 5192# USAGE: run the script as normal. You can provide a factor as an 5193# argument, eg "2" to run twice as fast, or "0.5" to run 5194# at half time. eg, 5195# ./session_0002.textVNC.replay 2 5196# 5197# Auto generated by Chaosreader. 5198# 5199$| = 1; 5200$factor = $ARGV[0] || 1; 5201sub ms { 5202 $ms = shift; 5203 $ms = $ms / $factor; 5204 select(undef, undef, undef, $ms); 5205} 5206END 5207 5208 # 5209 # Sort the data on the timestamps, calculating timestamp differences 5210 # to record in the replay program. 5211 # 5212 @Times = (); 5213 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 5214 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") { 5215 push(@Times,$time) 5216 } 5217 } 5218 @Times = sort { $a <=> $b } @Times; 5219 5220 # 5221 # --- Main Loop --- 5222 # 5223 # (this needs to be a for loop!) 5224 for ($i=0; $i <= $#Times; $i++) { 5225 5226 ### Calculate time diff if possible 5227 if ($i == $#Times) { 5228 $timediff = 0; 5229 } else { 5230 $timediff = $Times[$i+1] - $Times[$i]; 5231 # just in case, 5232 if ($timediff < 0) { $timediff = 0; } 5233 } 5234 $time = $Times[$i]; 5235 5236 ### Fetch data from mem 5237 $data = $TCP{id}{$session_id}{time}{$time}{data}; 5238 ($code) = unpack("C",$data); 5239 5240 $chars = ""; 5241 5242 # skip code 0's 5243 if ($code > 0) { 5244 # 5245 # Process through VNC client codes 5246 # 5247 $chars = ""; 5248 while (length($data) > 0) { 5249 ($code) = unpack("C",$data); 5250 $length = $VNC_Code_Size{$code}; 5251 $length--; 5252 last if $length <= 0; 5253 5254 # Fetch this code only 5255 ($code,$value,$data) = unpack("Ca${length}a*",$data); 5256 5257 ### Process Key Pressed 5258 if ($code == 4) { 5259 ($down,$junk,$extra,$char) = unpack("Ca4Ca",$value); 5260 5261 next if $down == 0; # record key-ups 5262 5263 if ($extra == 0) { 5264 $chars .= $char; 5265 } else { 5266 if (defined $KeyCode{vnc}{0}{$char}) { 5267 $chars .= $KeyCode{vnc}{0}{$char}; 5268 } 5269 } 5270 $html .= $chars; 5271 } 5272 } 5273 5274 } 5275 5276 $chars =~ s/\\/\\\\/g; 5277 $chars =~ s/'/\\'/g; 5278 5279 ### Now output the data in the replay program 5280 unless (length($chars) == 0) { 5281 print REPLAY2 "ms($oldtimediff);\n" 5282 unless $oldtimediff < 0.002; 5283 5284 ### Print the data 5285 print REPLAY2 "print '" . $chars . "';\n"; 5286 5287 # these counters are for efficiency, otherwise 5288 # we print too many sequiential sleeps 5289 $printed = 1; 5290 $oldtimediff = 0; 5291 } else { 5292 $printed = 0; 5293 $oldtimediff += $timediff; 5294 next; 5295 } 5296 5297 ### This causes the replay program to pause 5298 print REPLAY2 "ms($timediff);\n" 5299 unless $timediff < 0.002; 5300 } 5301 close REPLAY2; 5302 5303 ### Better make it executable 5304 chmod (0755, "$filename2"); 5305 5306 5307 # --- HTML --- 5308 # 5309 # Create a HTML page showing the keystrokes 5310 5311 ### Clean up html 5312 $html = &Desex_HTML($html); 5313 5314 ### Output 5315 $filename3 = "session_${numtext}.text${service_name}${ext}.html"; 5316 open (OUT,">$filename3") ||die "ERROR45: file create, $filename3: $!\n"; 5317 binmode(OUT); 5318 print OUT "<HTML>\n<BODY bgcolor=\"white\">\n" . 5319 "<H1>$service_name: $session_text</H1>\n" . 5320 "<H2>File $Arg{infile}, Session $number</H2>\n" . 5321 "<PRE WRAP=\"virtual\">\n" . 5322 "<font color=\"red\">" .$html. "</font></PRE>\n</BODY>\n</HTML>\n"; 5323 close OUT; 5324 5325 ### Global Vars 5326 $length = length($html); 5327 $Index{HTML}[$number] .= 5328 "<li><a href=\"$filename3\">keystrokes</a></li>\n"; 5329 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 5330 '"' , " $filename3","",$length); 5331 5332 5333 # 5334 # Output - Main VNC replay program 5335 # 5336 $filename = "session_${numtext}.${service_name}.replay"; 5337 $duration = ($TCP{id}{$session_id}{EndTime} - 5338 $TCP{id}{$session_id}{StartTime}); 5339 $duration = sprintf("%.0f",$duration); 5340 open (REPLAY,">$filename") || 5341 die "ERROR46: creating $filename $!\n"; 5342 binmode(REPLAY); # for backward OSs 5343 5344 # 5345 # --- VNC --- 5346 # 5347 # Create a perl program, that when run itself will create a 5348 # playback VNC server that listens on a port. When a vncviewer 5349 # connects, the contents of the server 1-way stream arew played back, 5350 # with pauses. 5351 # 5352 print REPLAY "#!$PERL\n"; 5353 print REPLAY <<'END'; 5354# 5355# This is a VNC replay program. This runs as a server and listens on a port, 5356# then vncviewer is run to connect to that port - at which point the playback 5357# commences. 5358# 5359# USAGE: ./session_0001.VNC.replay [-p port] factor 5360# 5361# just run the script as normal. You can provide a factor as an 5362# argument, eg "2" to run twice as fast, or "0.5" to run 5363# at half time. eg, 5364# ./session_0002.VNC.replay 2 5365# a different host and port can be specified if needed. eg, 5366# ./session_0002.VNC.replay -p 5925 5367# 5368# After the script is running, connect using vncviewer. eg, 5369# vncviewer -viewonly localhost:25 5370# 5371# PROBLEMS: The playback needs to have captured the start of the connection, 5372# you need to be at the same colour depth as the playback (or more may 5373# work), and your screen should be at least as big as the playback 5374# resolution. Newer versions of vncviewer may be tuned to match the 5375# playback (eg "-8bit"). 5376# 5377# Auto generated by Chaosreader. 5378# 5379 5380use IO::Socket; 5381use Getopt::Std; 5382use Net::hostent; 5383 5384$| = 1; 5385 5386if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); } 5387 5388# Command line options take preference 5389&getopts('p:'); 5390if (defined $opt_p) { $port = $opt_p; } else { $port = 5921; } 5391$vncport = $port - 5900; 5392if ($vncport < 0) { die "ERROR47: Port $port too low, use at least 5901.\n"; } 5393$factor = $ARGV[0] || 1; 5394$DEBUG = 0; 5395 5396print "Chaosreader VNC Replay (experimental)\n\n"; 5397print "Listening on port $port...\n"; 5398 5399 5400# --- Open Socket --- 5401# 5402$server = IO::Socket::INET->new( Proto => 'tcp', 5403 LocalPort => $port, 5404 Listen => SOMAXCONN, 5405 Reuse => 1); 5406 5407die "can't setup server" unless $server; 5408unless ($server) { 5409 die "ERROR48: Can't open port $port. Try a different port."; 5410} 5411 5412print <<WELCOME; 5413Port opened successfully. 5414 5415Now run vncviewer and connect to this port. eg, 5416 vncviewer -viewonly localhost:$vncport 5417 5418If you are prompted for a password, type any character and hit enter. 5419Waiting for connection... 5420WELCOME 5421 5422 5423# --- Subroutines --- 5424# 5425 5426# ms - sleeps for specified milliseconds 5427# 5428sub ms { 5429 $ms = shift; 5430 $ms = $ms / $factor; 5431 select(undef, undef, undef, $ms); 5432} 5433# help - print help 5434# 5435sub help { 5436 open (MYSELF,"$0") || die "ERROR49: I can't see myself: $!\n"; 5437 @Myself = <MYSELF>; 5438 close MYSELF; 5439 ### Print comment from top of code 5440 foreach $line (@Myself) { 5441 last if $line !~ /^#/; 5442 next if $line =~ m:^#!/usr/bin/perl:; 5443 $line =~ s/^#/ /; 5444 print $line; 5445 } 5446 print "\n"; 5447 exit(0); 5448} 5449 5450 5451# 5452# --- MAIN --- 5453# 5454 5455### Wait for connection 5456$client = $server->accept(); 5457$client->autoflush(1); 5458 5459print "Sending VNC traffic:"; 5460 5461END 5462 5463 # 5464 # Sort the data on the timestamps, calculating timestamp differences 5465 # to record in the replay program. 5466 # 5467 @Times = (); 5468 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) { 5469 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") { 5470 push(@Times,$time) 5471 } 5472 } 5473 @Times = sort { $a <=> $b } @Times; 5474 5475 # 5476 # --- Main Loop --- 5477 # 5478 # (this needs to be a for loop!) 5479 for ($i=0; $i <= $#Times; $i++) { 5480 5481 ### Calculate time diff if possible 5482 if ($i == $#Times) { 5483 $timediff = 0; 5484 } else { 5485 $timediff = $Times[$i+1] - $Times[$i]; 5486 # just in case, 5487 if ($timediff < 0) { $timediff = 0; } 5488 } 5489 $time = $Times[$i]; 5490 5491 ### Fetch data from mem 5492 $data = $TCP{id}{$session_id}{time}{$time}{data}; 5493 5494 $data =~ s/\\/\\\\/g; 5495 $data =~ s/'/\\'/g; 5496 $data =~ s/\015\012/'."\\015\\012".'/gs; 5497 5498 # 5499 # Now output the data in the replay program 5500 # 5501 print REPLAY "print '.';\n"; 5502 print REPLAY "print \$client '" . $data . "';\n"; 5503 5504 # 5505 # This causes the replay program to pause 5506 # 5507 print REPLAY "ms($timediff);\n" 5508 unless $timediff < 0.002; # (efficiency). 5509 } 5510 print REPLAY "print \"\n\";\n"; 5511 print REPLAY "close \$client;\n"; 5512 close REPLAY; 5513 5514 ### Better make it executable 5515 chmod (0755, "$filename"); 5516 5517 ### Global Vars 5518 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 5519 "</a> $duration seconds</li>\n"; 5520 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" . 5521 "</a> $duration seconds</li>\n"; 5522 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 5523 '"' , " $filename","",$duration); 5524 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n", 5525 '"' , " $filename2","",$duration); 5526} 5527 5528 5529 5530# Save_SMTP_Emails - Save emails from an SMTP session. 5531# 5532sub Save_SMTP_Emails { 5533 my ($filename); 5534 my $session_id = shift; 5535 my $number = shift; 5536 my $service_name = "smtp"; 5537 my $numtext = sprintf("%04d",$number); 5538 5539 5540 ### Full - Input 5541 $snmp_data = &TCP_Follow_RawB($session_id); 5542 5543 ### Full - Processing 5544 @Snmp_parts = split(/\r\n\.\r\n|\n\.\n/,$snmp_data); 5545 5546 ### LOOP 5547 $partnum = 0; 5548 foreach $snmp_part (@Snmp_parts) { 5549 5550 next unless $snmp_part =~ /DATA/; 5551 $partnum++; 5552 $parttext = sprintf("%02d",$partnum); 5553 5554 ### Part - Processing 5555 $snmp_part =~ s/^.*DATA\r?\n//s; # '/s;' is new perl5, 5556 # else '/;' with $* = 1 5557 5558 ### Part - Output 5559 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 5560 else { $ext = ""; } 5561 $filename = "session_${numtext}.part_${parttext}." . 5562 "${service_name}${ext}.email"; 5563 open (OUT,">$filename") || 5564 die "ERROR50: file create, $filename: $!\n"; 5565 binmode(OUT); # for backward OSs 5566 print OUT $snmp_part; 5567 close OUT; 5568 5569 ### Part - Global Vars 5570 my $length = length($snmp_part); 5571 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 5572 "</a> $length bytes</li>\n"; 5573 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 5574 '"' , " $filename","",$length); 5575 } 5576} 5577 5578 5579# Save_HTTP_Files - Save HTTP components. 5580# 5581sub Save_HTTP_Files { 5582 my ($filename); 5583 my $session_id = shift; 5584 my $number = shift; 5585 my $service_name = shift; 5586 my $numtext = sprintf("%04d",$number); 5587 5588 ### Full - Input 5589 $http_session = &TCP_Follow_RawA($session_id); 5590 5591 ### Full - Processing 5592 @HttpParts = split(/HTTP\/[0-9.]* /,$http_session); 5593 5594 ### LOOP 5595 $partnum = 0; 5596 foreach $http_part (@HttpParts) { 5597 5598 # JL. I want to see all parts, in partcular empty ones 5599 # resulting from 304 (Not Modified). Thus, the original 5600 # check below on $http_data is too strong. 5601 next if $http_part eq ""; 5602 5603 ### Part - Processing 5604 ($http_header,$http_data) = split(/\r\n\r\n|\n\n/,$http_part,2); 5605 # next if $http_data eq ""; 5606 # next if length($http_data) < 8; 5607 $partnum++; 5608 $parttext = sprintf("%02d",$partnum); 5609 5610 ### JL: Chunk Check, patch from http://refrequelate.blogspot.com/2008/07/more-de-chunking-chaosreader-patch.html 5611 if ( $http_header =~ /Transfer-Encoding: chunked/ ) { 5612 my $new_http_data=""; 5613 my $chunksize=-1; 5614 my $pos=0; 5615 until ($chunksize==0) { 5616 my $eolpos=index($http_data,"\r\n",$pos); 5617 $chunksize=hex(substr($http_data,$pos,$eolpos - $pos)); 5618 $pos=($eolpos+2); 5619 if ($chunksize > 0) { 5620 $new_http_data.=substr($http_data,$pos,$chunksize); 5621 } 5622 $pos+=($chunksize+2); 5623 } 5624 $http_data=$new_http_data; 5625 } 5626 5627 ### Part - Checks 5628 my $http_type = &File_Type($http_data); 5629 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 5630 else { $ext = ""; } 5631 5632 ### JL: Check for known MIME type in Content-Type 5633 my ($content_type) = $http_header =~ /Content-Type:\s+(\S*)/is; 5634 my $file_extension = ""; 5635 if ($content_type ne "") { 5636 for my $pattern ( keys %mime_types ) { 5637 my $value = $mime_types{$pattern}; 5638 if ( $content_type =~ /$pattern/i ) { 5639 $file_extension = $value; 5640 last; 5641 } 5642 } 5643 if (($file_extension eq "bin") 5644 or ($file_extension eq "asc")) { 5645 # Not too specific. Some HTTP servers return images 5646 # with Content-Type: application/octet-stream, others 5647 # with Content-Type: text/plain 5648 # Prefer http_type then... 5649 if ($http_type ne "data") { 5650 $file_extension = $http_type; 5651 } 5652 } 5653 elsif ($file_extension eq "") { 5654 print "Unkown Content-Type $content_type."; 5655 print " May want to extend MIME types.\n"; 5656 } 5657 } 5658 5659 ### Part - Output 5660 # JL: Create filename based on Content-Type 5661 my $filename = "session_${numtext}.part_$parttext${ext}"; 5662 if ($file_extension ne "") { 5663 $filename .= ".$file_extension"; 5664 } 5665 if ( ($file_extension eq "") or ($http_type eq "gz") ) { 5666 $filename .= ".$http_type"; 5667 } 5668 open (OUT,">$filename") || 5669 die "ERROR51: file create, $filename: $!\n"; 5670 binmode(OUT); # for backward OSs 5671 print OUT $http_data; 5672 close OUT; 5673 5674 ### JL: gz decompressing 5675 if ( $http_type eq "gz" ) { 5676 my $gunzipped = substr($filename, 0, length($filename) - 3); 5677 my $gunzip_failed = 0; 5678 gunzip $filename => $gunzipped 5679 or $gunzip_failed = 1; 5680 if ( $gunzip_failed == 0 ) { 5681 $filename = $gunzipped; 5682 } 5683 } 5684 # Pex:Deflate 5685 elsif ( $http_type eq "deflate") { 5686 #print "inflating " . $http_type ; 5687 my $inflated = substr($filename, 0, length($filename) - 4) . "inflated.html"; 5688 my $status = IO::Uncompress::Inflate::inflate($filename, $inflated, Transparent => 0); 5689 my $error = $IO::Uncompress::Inflate::InflateError; 5690 if ($status) { 5691 #Succesful inflate 5692 $filename = $inflated; 5693 } 5694 else { 5695 my $status = IO::Uncompress::RawInflate::rawinflate($filename, $inflated); 5696 my $error = $IO::Uncompress::RawInflate::RawInflateError; 5697 if ($status) { 5698 #Succesful raw inflate 5699 $filename = $inflated; 5700 } 5701 elsif ($error eq "expected end of file"){ 5702 # End of file, might been succesful 5703 $filename = $inflated; 5704 } 5705 else { 5706 #failed inflate 5707 #print "failed inflate"; 5708 } 5709 } 5710 } 5711 5712 ### Part - Global Vars 5713 my $length = length($http_data); 5714 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" . 5715 "</a> $length bytes</li>\n"; 5716 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 5717 '"' , " $filename","",$length); 5718 if (&Is_Image($http_type) or 5719 &Is_Image($file_extension)) { # JL: Also check file ext. 5720 $Image{HTML}[$number]{links} .= "<img src=\"$filename\"> "; 5721 $Image{notempty} = 1; 5722 # JL: Remember this part as image. 5723 $ExtImage{HTML}[$number]{parts}[$partnum] = 1; 5724 } 5725 } 5726} 5727 5728 5729# Save_NFS_File - Save NFS file. Only works well for some files, if the NFS 5730# header can't be processed, a "*.nfs.raw" file is created. 5731# 5732sub Save_NFS_File { 5733 my ($filename); 5734 my $session_id = shift; 5735 my $number = shift; 5736 my $service_name = "nfs"; 5737 my $numtext = sprintf("%04d",$number); 5738 5739 ### Input 5740 my $nfs_raw = &TCP_Follow_RawB($session_id); 5741 5742 ### Processing 5743 ($nfs_start,$nfs_size,$nfs_end) = unpack('a56a4a*',$nfs_raw); 5744 $nfs_sizeint = unpack("N",$nfs_size); 5745 ($nfs_start,$nfs_data) = split(/$nfs_size....$nfs_size/,$nfs_end,2); 5746 5747 ### Checks 5748 if (($nfs_sizeint > 4) && (length($nfs_data) >= $nfs_sizeint)) { 5749 $nfs_type = &File_Type($nfs_data); 5750 if ($nfs_sizeint < length($nfs_data)) { 5751 $nfs_data = unpack("a${nfs_sizeint}a*",$nfs_data); 5752 } 5753 } else { 5754 $nfs_type = "raw"; 5755 $nfs_data = $nfs_raw; 5756 } 5757 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; } 5758 else { $ext = ""; } 5759 5760 ### Output 5761 $filename = "session_${numtext}.part_01.${service_name}${ext}.nfs." . 5762 "$nfs_type"; 5763 open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n"; 5764 binmode(OUT); # for backward OSs 5765 print OUT $nfs_data; 5766 close OUT; 5767 5768 ### Global Vars 5769 my $length = length($nfs_data); 5770 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename</a>" . 5771 " $length bytes</li>\n"; 5772 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 5773 '"' , " $filename","",$length); 5774} 5775 5776 5777# Save_DNS_File - Save DNS file. 5778# 5779sub Save_DNS_File { 5780 my ($filename); 5781 my $session_id = shift; 5782 my $number = shift; 5783 my $numtext = sprintf("%04d",$number); 5784 my $text = $UDP{id}{$session_id}{DNS}; 5785 5786 ### Output 5787 $filename = "session_${numtext}.domain.txt"; 5788 open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n"; 5789 print OUT $text; 5790 close OUT; 5791 5792 ### Global Vars 5793 my $length = length($text); 5794 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename</a>" . 5795 " $length bytes</li>\n"; 5796 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n", 5797 '"' , " $filename","",$length); 5798} 5799 5800 5801# TCP_Follow_RawA - process session by TCP Seq numbers 1-way. 5802# (TCP ASSEMBLY) 5803# 5804sub TCP_Follow_RawA { 5805 my $session_id = shift; 5806 my $raw = ""; 5807 5808 # 5809 # Assemble TCP Sessions. Each hash contains session_ids as keys, 5810 # and the value points to another hash of sequence numbers and data. 5811 # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output. 5812 # 5813 @Seqs = keys (%{$TCP{id}{$session_id}{Aseq}}); 5814 foreach $seq (sort { $a <=> $b } @Seqs) { 5815 $raw .= ${$TCP{id}{$session_id}{Aseq}{$seq}}; 5816 } 5817 5818 return $raw; 5819} 5820 5821 5822# TCP_Follow_RawB - process session by TCP Seq numbers 1-way. 5823# (TCP ASSEMBLY) 5824# 5825sub TCP_Follow_RawB { 5826 my $session_id = shift; 5827 my $raw = ""; 5828 5829 # 5830 # Assemble TCP Sessions. Each hash contains session_ids as keys, 5831 # and the value points to another hash of sequence numbers and data. 5832 # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output. 5833 # 5834 @Seqs = keys (%{$TCP{id}{$session_id}{Bseq}}); 5835 foreach $seq (sort { $a <=> $b } @Seqs) { 5836 $raw .= ${$TCP{id}{$session_id}{Bseq}{$seq}}; 5837 } 5838 5839 return $raw; 5840} 5841 5842 5843# Pick_Service_Port - pick which port is the server. Usually is the lower 5844# number, however check if the direction is already known (eg SYN). 5845# The port arguments will not often be needed. 5846# 5847# NOTE: This code is different to Generate_TCP_IPs - which does the "<->"'s 5848# 5849sub Pick_Service_Port { 5850 my $type = shift; 5851 my $id = shift; 5852 my $porta = shift; 5853 my $portb = shift; 5854 my $from_server = 0; 5855 my ($hi,$low); 5856 5857 # Catch active FTP, etc. 5858 ($low,$hi) = sort { $a <=> $b } ($porta,$portb); 5859 if ($low < 100) { 5860 return ($low,$hi); 5861 } 5862 5863 if ($type eq "TCP") { 5864 if (defined $TCP{id}{$id}{source}) { 5865 if ($TCP{id}{$id}{source} eq $TCP{id}{$id}{src}) { 5866 return ($TCP{id}{$id}{dest_port},$TCP{id}{$id}{src_port}); 5867 } else { 5868 return ($TCP{id}{$id}{src_port},$TCP{id}{$id}{dest_port}); 5869 } 5870 } 5871 } elsif ($type eq "UDP") { 5872 return ($UDP{id}{$id}{dest_port},$UDP{id}{$id}{src_port}); 5873 } 5874 5875 # resort to a sort 5876 return sort { $a <=> $b } ($porta,$portb); 5877} 5878 5879# Retrieve DNS name for IP address based on DNS traffic of this capture. 5880# If possible, retrieve the original name for a CNAME of the IP address. 5881# 5882sub Get_Name_For_IP { 5883 my $ip_addr = shift; 5884 my $result = $ip_addr; 5885 if (defined $DNS{$ip_addr}) { 5886 $result = $DNS{$ip_addr}; 5887 while (defined $DNS{$result}) { 5888 $result = $DNS{$result}; 5889 } 5890 } 5891 return $result; 5892} 5893 5894# Generate_SessionID - input source and dest IPs and ports, and generate 5895# a unique session_id based on them. this is done by sorting on 5896# ports and then IPs. Also returns a flag if the packet may be 5897# assumed to be from_server - where the lowest port is assumed to 5898# be the server (unless TCP SYNs have been observed). 5899# 5900sub Generate_SessionID { 5901 my $ip_src = shift; 5902 my $tcp_src_port = shift; 5903 my $ip_dest = shift; 5904 my $tcp_dest_port = shift; 5905 my $type = shift; 5906 my $from_server = 0; 5907 my $session_id; 5908 5909 # 5910 # Generate session_id string using host:port,host:port sorted on 5911 # port (low port last). 5912 # 5913 if ($tcp_src_port < $tcp_dest_port) { 5914 $session_id = "$ip_dest:$tcp_dest_port,$ip_src:$tcp_src_port"; 5915 $from_server = 1; 5916 } elsif ($tcp_src_port > $tcp_dest_port) { 5917 $session_id = "$ip_src:$tcp_src_port,$ip_dest:$tcp_dest_port"; 5918 $from_server = 0; 5919 } else { 5920 $session_id =join(",",sort("$ip_src:$tcp_src_port", 5921 "$ip_dest:$tcp_dest_port")); 5922 $from_server = 1; 5923 } 5924 5925 if ($type eq "TCP") { 5926 if (defined $TCP{id}{$session_id}{source}) { 5927 if ($TCP{id}{$session_id}{source} eq $ip_dest 5928 # JL: Also look at the port as ip_src and ip_dest 5929 # may be the same (e.g., 127.0.0.1) 5930 # Also in Generate_TCP_IDs below. 5931 && $TCP{id}{$session_id}{source_port} eq $tcp_dest_port) { 5932 $from_server = 1; 5933 } else { 5934 $from_server = 0; 5935 } 5936 } 5937 } 5938 return ($session_id,$from_server); 5939} 5940 5941 5942 5943# Generate_TCP_IDs - generate a text and html version of the session ID, that 5944# displays direction of the TCP session if SYNs and ACKs were 5945# observed, else uses a "<->" symbol to represent unknown 5946# direction. TCP only. 5947# 5948sub Generate_TCP_IDs { 5949 my $session_id = shift; 5950 my ($ip_src,$tcp_src_port,$ip_dest,$tcp_dest_port,$text,$html); 5951 5952 # try this direction, 5953 $ip_src = $TCP{id}{$session_id}{src}; 5954 $ip_dest = $TCP{id}{$session_id}{dest}; 5955 $tcp_src_port = $TCP{id}{$session_id}{src_port}; 5956 $tcp_dest_port = $TCP{id}{$session_id}{dest_port}; 5957 5958 if (defined $TCP{id}{$session_id}{source}) { 5959 if ($TCP{id}{$session_id}{source} eq $ip_dest 5960 && $TCP{id}{$session_id}{source_port} eq $tcp_dest_port) { 5961 # nope, switch ends 5962 $ip_src = $TCP{id}{$session_id}{dest}; 5963 $ip_dest = $TCP{id}{$session_id}{src}; 5964 $tcp_src_port = $TCP{id}{$session_id}{dest_port}; 5965 $tcp_dest_port = $TCP{id}{$session_id}{src_port}; 5966 } 5967 if ($Arg{prefer_dns}) { 5968 $ip_src = &Get_Name_For_IP($ip_src); 5969 $ip_dest = &Get_Name_For_IP($ip_dest); 5970 } 5971 $text = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port"; 5972 $html = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port"; 5973 } else { 5974 if ($Arg{prefer_dns}) { 5975 $ip_src = &Get_Name_For_IP($ip_src); 5976 $ip_dest = &Get_Name_For_IP($ip_dest); 5977 } 5978 $text = "$ip_src:$tcp_src_port <-> $ip_dest:$tcp_dest_port"; 5979 $html = "$ip_src:$tcp_src_port <-> " . 5980 "$ip_dest:$tcp_dest_port"; 5981 } 5982 5983 return ($text,$html); 5984} 5985 5986 5987 5988# Generate_IP_ID - input source IP, dest IP and ident, and generate a 5989# unique ip_id based on them. This is necessary for IP 5990# fragmentation reassembely. Normally we would assume that 5991# the IP_ident was unique - however this program could 5992# process traffic from many different hosts over a long 5993# period of time - idents alone could clash. 5994# 5995sub Generate_IP_ID { 5996 my $ip_src = shift; 5997 my $ip_dest = shift; 5998 my $ip_ident = shift; 5999 my $ip_id; 6000 6001 # 6002 # Generate ip_id string using host:host:ident sorted on IP. 6003 # 6004 # 6005 $ip_id = join(",",sort("$ip_src","$ip_dest")) . ",$ip_ident"; 6006 6007 return $ip_id; 6008} 6009 6010 6011 6012# Read_Tcpdump_Record - Read the next tcpdump record, will "last" if 6013# there are no more records. 6014# 6015sub Read_Tcpdump_Record { 6016 my $more; 6017 6018 ### Fetch record header 6019 $length = read(INFILE,$header_rec,($integerSize * 2 + 8)); 6020 6021 ### Quit main loop if at end of file 6022 last if $length < 16; 6023 6024 ### Throw out extra info in tcpdump/modified1 format 6025 if ($STYLE =~ /^modified/) { 6026 $length = read(INFILE,$more,8); 6027 } 6028 6029 $frame++; 6030 6031 ## Unpack header, endian sensitive 6032 if ($STYLE =~ /1$/) { 6033 ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length, 6034 $tcpdump_length_orig) 6035 = unpack('NNNN',$header_rec); 6036 } else { 6037 ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length, 6038 $tcpdump_length_orig) 6039 = unpack('VVVV',$header_rec); 6040 } 6041 $length = read(INFILE,$tcpdump_data,$tcpdump_length); 6042 $tcpdump_drops = $tcpdump_length_orig - $tcpdump_length; 6043} 6044 6045 6046# Read_Snoop_Record - Read the next snoop record, will "last" if 6047# there are no more records. 6048# 6049sub Read_Snoop_Record { 6050 ### Fetch record header 6051 $length = read(INFILE,$header_rec,24); 6052 6053 ### Quit main loop if at end of file 6054 last if $length < 24; 6055 6056 $frame++; 6057 6058 ### Unpack header 6059 ($snoop_length_orig,$snoop_length_inc,$snoop_length_rec,$snoop_drops, 6060 $snoop_seconds,$snoop_msecs) = unpack('NNNNNN',$header_rec); 6061 $length = read(INFILE,$snoop_data,$snoop_length_inc); 6062 $skip = read(INFILE,$pad,($snoop_length_rec - $snoop_length_inc - 24)); 6063} 6064 6065 6066# Load_Index_File - Load the master index file "index.file" into @Master 6067# 6068sub Load_Index_File { 6069 6070 my ($path,$dir,$file,$start,$end,$duration,$index); 6071 6072 # 6073 # Load index.file lines into memory 6074 # 6075 open (FILES,"index.file") || die "ERROR53: Can't read index.file: $!\n" 6076 ."Standalone mode needs to have run recently from this directory.\n\n"; 6077 6078 chomp(@Files = <FILES>); 6079 close FILES; 6080 6081 # 6082 # Populate @Master 6083 # 6084 $index = 0; 6085 foreach $path (@Files) { 6086 ($dir,$file,$duration,$start,$end) = split(/\t/,$path); 6087 $Master[$index]{starttime} = $start; 6088 $Master[$index]{endtime} = $end; 6089 $Master[$index]{dir} = $dir; 6090 $Master[$index]{file} = $file; 6091 $Master[$index]{duration} = $duration; 6092 $Master[$index]{size} = -s "$dir/$file"; 6093 $index++; 6094 } 6095} 6096 6097 6098# Load_Etc_Services - load /etc/services lookup table into memory, 6099# into %Services_TCP and %Services_UDP. 6100# 6101sub Load_Etc_Services { 6102 my ($line,$name,$service); 6103 6104 ### Hardcoded 6105 %Services_TCP = (20 => "ftp-data", 6106 21 => "ftp", 6107 23 => "telnet", 6108 25 => "smtp", 6109 80 => "web", 6110 109 => "pop2", 6111 110 => "pop3", 6112 143 => "imap", 6113 513 => "login", 6114 514 => "shell", 6115 3128 => "web", 6116 4110 => "irc4110", 6117 5000 => "irc5000", 6118 6000 => "X11", 6119 6660 => "irc", 6120 6665 => "irc", 6121 6666 => "irc", 6122 6667 => "irc", 6123 6668 => "irc", 6124 6669 => "irc", 6125 7000 => "irc7000", 6126 8000 => "irc8000", 6127 8080 => "web", 6128 9000 => "irc9000"); 6129 # non standard IRC ports include the number in their name 6130 6131 foreach (@Save_As_X11_Playback_Ports) { 6132 $Services_TCP{$_} = "X11"; 6133 } 6134 6135 foreach (@Save_As_VNC_Playback_Ports) { 6136 $Services_TCP{$_} = "VNC"; 6137 } 6138 6139 %Services_UDP = (53 => "dns"); 6140 6141 ### File input 6142 open(SERVICES,"/etc/services") || return; 6143 while ($line = <SERVICES>) { 6144 next if $line =~ /^#|^\s*$/; # skip comments, blank lines. 6145 if ($line =~ /\d\/tcp/) { 6146 $is_tcp = 1; 6147 } else { 6148 $is_tcp = 0; 6149 } 6150 $line =~ s:/.*::; 6151 ($name,$port) = split(' ',$line); 6152 if ($is_tcp) { 6153 $Services_TCP{$port} = $name; 6154 } else { 6155 $Services_UDP{$port} = $name; 6156 } 6157 6158 } 6159 close SERVICES; 6160} 6161 6162 6163# Set_IP_Protocols - Set a lookup hash for IP Protocols to names. 6164# RFC790, RFC1700. 6165# 6166sub Set_IP_Protocols { 6167 %IP_Protocols = (0 => "Reserved", 6168 1 => "ICMP", 6169 2 => "Unassigned", 6170 3 => "Gateway-to-Gateway", 6171 4 => "CCMC Gateway Monitoring Message", 6172 5 => "ST", 6173 6 => "TCP", 6174 7 => "UCL", 6175 8 => "Unassigned", 6176 9 => "Secure", 6177 10 => "BBN RCC Monitoring", 6178 11 => "NVP", 6179 12 => "PUP", 6180 13 => "Pluribus", 6181 14 => "Telenet", 6182 15 => "XNET", 6183 16 => "Chaos", 6184 17 => "UDP", 6185 18 => "Multiplexing", 6186 19 => "DCN", 6187 20 => "TAC Monitoring", 6188 37 => "DDP", 6189 41 => "SIP", 6190 42 => "SDRP", 6191 44 => "IPv6 Frag", 6192 50 => "SIPP-ESP", 6193 51 => "SIPP-AH", 6194 53 => "SWIPE", 6195 50 => "SDRP", 6196 58 => "ICMPv6", 6197 88 => "IGRP", 6198 94 => "IPIP" 6199 ); 6200} 6201 6202# Set_ICMP_Types - Set a lookup hash for ICMP Types. RFC792. 6203# 6204sub Set_ICMP_Types { 6205 %ICMP_Types = (0 => "Echo Reply", 6206 3 => "Destination Unreachable", 6207 4 => "Source Quench", 6208 5 => "Redirect", 6209 8 => "Echo", 6210 11 => "Time Exceeded", 6211 12 => "Parameter Problem", 6212 13 => "Timestamp", 6213 14 => "Timestamp Reply", 6214 15 => "Information Request", 6215 16 => "Information Reply", 6216 128 => "Echo", 6217 129 => "Echo Reply", 6218 135 => "Neighbor solicitation", 6219 136 => "Neighbor advertisement" 6220 ); 6221} 6222 6223# Set_Result_Names - Set a lookup hash for squid result codes. 6224# (This needs some fine tuning). 6225# 6226sub Set_Result_Names { 6227 %Result_Names = ("" => "TCP_MISS", 6228 000 => "TCP_MISS", 6229 200 => "TCP_HIT", 6230 302 => "TCP_HIT", 6231 304 => "TCP_REFRESH_HIT", 6232 404 => "TCP_NEGATIVE_HIT" 6233 ); 6234} 6235 6236# Set_X11_Codes - creates a lookup hash needed for X11 transposing. 6237# 6238sub Set_X11_Codes { 6239 # 6240 # This has a row per X11 code, the row describing the 16 bit 6241 # words that make up the values. "1" means resource id. 6242 # (some values are 8 bit, but are fortunately padded). 6243 # 6244 6245 @X11_Codes = ( 6246[ 0 ], # X_Error entry 6247[ 0, 2, 2, 0, 0, 0, 1, 0,4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateWindow 1 6248[ 0, 1, 0 ], # X_ChangeWindowAttributes 6249[ 0, 1 ], # X_GetWindowAttributes 6250[ 0 ], # X_DestroyWindow? 6251[ 0 ], # X_DestroySubwindows? 6252[ 0, 1 ], # X_ChangeSaveSet 6253[ 0, 1, 1, 0 ], # X_ReparentWindow 6254[ 0, 1 ], # X_MapWindow 6255[ 0, 1 ], # X_MapSubwindows 6256[ 0, 1 ], # X_UnmapWindow 10 6257[ 0, 1 ], # X_UnmapSubwindows 6258[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ConfigureWindow 6259[ 0, 1 ], # X_CirculateWindow 6260[ 0, 2 ], # X_GetGeometry 6261[ 0, 1 ], # X_QueryTree 6262[ 0, 1 ], # X_InternAtom (? else 0,0) 6263[ 0 ], # X_GetAtomName? 6264[ 0, 1, 0, 0, 1, 0 ], # X_ChangeProperty (? else 0,1,0,0,0,0) 6265[ 0, 1, 0 ], # X_DeleteProperty 6266[ 0, 2, 0, 0, 0, 0 ], # X_GetProperty 20 6267[ 0 ], # X_ListProperties? 6268[ 0, 1, 0, 0 ], # X_SetSelectionOwner 6269[ 0 ], # X_GetSelectionOwner 6270[ 0, 1, 0, 0, 0, 0 ], # X_ConvertSelection 6271[ 0, 1, 0 ], # X_SendEvent 6272[ 0, 1, 0, 1, 0, 0 ], # X_GrabPointer 6273[ 0, 1, 0 ], # X_UngrabPointer? 6274[ 0, 1, 0, 1, 0, 0 ], # X_GrabButton 6275[ 0, 1, 0 ], # X_UngrabButton 6276[ 0, 1, 0, 0 ], # X_ChangeActivePointerGrab 30 6277[ 0, 1, 0, 0 ], # X_GrabKeyboard 6278[ 0, 1, 0 ], # X_UngrabKeyboard? 6279[ 0, 1, 0, 0 ], # X_GrabKey 6280[ 0, 1, 0 ], # X_UngrabKey 6281[ 0, 0, 0 ], # X_AllowEvents 6282[ 0 ], # X_GrabServer? 6283[ 0 ], # X_UngrabServer? 6284[ 0 ], # X_QueryPointer? 6285[ 0, 1, 0, 0 ], # X_GetMotionEvents 6286[ 0, 1, 1, 0 ], # X_TranslateCoords 40 6287[ 0, 1, 1, 0, 0, 0 ], # X_WarpPointer 6288[ 0, 1, 0 ], # X_SetInputFocus 6289[ 0 ], # X_GetInputFocus? 6290[ 0 ], # X_QueryKeymap? 6291[ 0, 1, 0 ], # X_OpenFont 6292[ 0, 1 ], # X_CloseFont 6293[ 0, 1 ], # X_QueryFont 6294[ 0, 1 ], # X_QueryTextExtents 6295[ 0, 0 ], # X_ListFonts 6296[ 0, 0 ], # X_ListFontsWithInfo 50 6297[ 0, 0 ], # X_SetFontPath 6298[ 0 ], # X_GetFontPath? 6299[ 0, 1, 2, 0 ], # X_CreatePixmap 6300[ 0 ], # X_FreePixmap? 6301[ 0, 1, 2, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateGC ?(else 0,1,1,0) 6302[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeGC 6303[ 0, 1, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CopyGC 6304[ 0, 1, 0 ], # X_SetDashes 6305[ 0, 1, 0 ], # X_SetClipRectangles 6306[ 0, 1 ], # X_FreeGC? 60 6307[ 0, 1, 0, 0 ], # X_ClearArea 6308[ 0, 2, 2, 1, 0, 0, 0 ], # X_CopyArea 6309[ 0, 2, 2, 1, 0, 0, 0, 0 ], # X_CopyPlane 6310[ 0, 2, 1 ], # X_PolyPoint 6311[ 0, 2, 1 ], # X_PolyLine 6312[ 0, 2, 1 ], # X_PolySegment 6313[ 0, 2, 1 ], # X_PolyRectangle 6314[ 0, 2, 1 ], # X_PolyArc 6315[ 0, 2, 1, 0 ], # X_FillPoly 6316[ 0, 2, 1 ], # X_PolyFillRectangle 70 6317[ 0, 2, 1 ], # X_PolyFillArc 6318[ 0, 2, 1, 0, 0, 0 ], # X_PutImage 6319[ 0, 2, 0, 0, 0 ], # X_GetImage 6320[ 0, 2, 1, 0 ], # X_PolyText8 6321[ 0, 2, 1, 0 ], # X_PolyText16 6322[ 0, 2, 1, 0 ], # X_ImageText8 6323[ 0, 2, 1, 0 ], # X_ImageText16 6324[ 0, 3, 1, 1 ], # X_CreateColormap 6325[ 0 ], # X_FreeColormap? 6326[ 0, 3, 3 ], # X_CopyColormapAndFree 80 6327[ 0 ], # X_InstallColormap? 6328[ 0 ], # X_UninstallColormap? 6329[ 0 ], # X_ListInstalledColormaps? 6330[ 0, 3, 0, 0 ], # X_AllocColor 6331[ 0, 3, 0 ], # X_AllocNamedColor 6332[ 0, 3, 0 ], # X_AllocColorCells 6333[ 0, 3, 0, 0 ], # X_AllocColorPlanes 6334[ 0, 3, 0 ], # X_FreeColors 6335[ 0, 3 ], # X_StoreColors 6336[ 0, 3, 0, 0 ], # X_StoreNamedColor 90 6337[ 0, 3 ], # X_QueryColors 6338[ 0, 3, 0 ], # X_LookupColor 6339[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateCursor 6340[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateGlyphCursor 6341[ 0 ], # X_FreeCursor? 6342[ 0, 1, 0, 0, 0 ], # X_RecolorCursor 6343[ 0, 2, 0 ], # X_QueryBestSize 6344[ 0, 1 ], # X_QueryExtension (? else 0,0) 6345[ 0, 0, 0 ], # X_ListExtensions? 6346[ 0, 1, 0 ], # X_ChangeKeyboardMapping 100 6347[ 0, 1, 0 ], # X_GetKeyboardMapping 6348[ 0, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeKeyboardControl 6349[ 0, 0, 0 ], # X_GetKeyboardControl? 6350[ 0 ], # X_Bell 6351[ 0, 0, 0 ], # X_ChangePointerControl 6352[ 0, 0, 0 ], # X_GetPointerControl? 6353[ 0, 0, 0 ], # X_SetScreenSaver 6354[ 0, 0, 0 ], # X_GetScreenSaver? 6355[ 0, 0 ], # X_ChangeHosts 6356[ 0 ], # X_ListHosts 110 6357[ 0 ], # X_SetAccessControl 6358[ 0 ], # X_SetCloseDownMode 6359[ 0, 0, 0 ], # X_KillClient? 6360[ 0, 1, 0 ], # X_RotateProperties 6361[ 0 ], # X_ForceScreenSaver 6362[ 0 ], # X_SetPointerMapping 6363[ 0, 0, 0 ], # X_GetPointerMapping? 6364[ 0 ], # X_SetModifierMapping 6365[ 0, 0, 0 ], # X_GetModifierMapping? 6366[ 0 ], # undef 120 6367[ 0 ], # undef 6368[ 0 ], # undef 6369[ 0 ], # undef 6370[ 0 ], # undef 6371[ 0 ], # undef 6372[ 0 ], # undef 6373[ 0, 0, 0 ] # X_NoOperation 127 6374 ); 6375 6376} 6377 6378# Set_X11_KeyCodes - creates a lookup hash of X11 Key codes needed 6379# to generate coloured 2-way HTML X11 reports. 6380# 6381sub Set_X11_KeyCodes { 6382 my ($junk,$code,$char1,$char2,$line, 6383 $sun_xmodmap_pke,$linux_xmodmap_pke); 6384 my %Alias; 6385 6386 # 6387 # These are generated using "xmodmap -pke" (and trimmed a little). 6388 # 6389 $sun_xmodmap_pke = <<END; 6390keycode 8 = Control_L 6391keycode 9 = Control_R 6392keycode 10 = Shift_L 6393keycode 11 = Shift_R 6394keycode 12 = Meta_L 6395keycode 13 = Meta_R 6396keycode 14 = Alt_L 6397keycode 15 = Alt_R 6398keycode 16 = space 6399keycode 17 = 0 parenright 6400keycode 18 = 1 exclam 6401keycode 19 = 2 at 6402keycode 20 = 3 numbersign 6403keycode 21 = 4 dollar 6404keycode 22 = 5 percent 6405keycode 23 = 6 asciicircum 6406keycode 24 = 7 ampersand 6407keycode 25 = 8 asterisk 6408keycode 26 = 9 parenleft 6409keycode 27 = minus underscore 6410keycode 28 = equal plus 6411keycode 29 = bracketleft braceleft 6412keycode 30 = bracketright braceright 6413keycode 31 = semicolon colon 6414keycode 32 = apostrophe quotedbl 6415keycode 33 = grave asciitilde 6416keycode 34 = comma less 6417keycode 35 = period greater 6418keycode 36 = slash question 6419keycode 37 = backslash bar 6420keycode 38 = a A 6421keycode 39 = b B 6422keycode 40 = c C 6423keycode 41 = d D 6424keycode 42 = e E 6425keycode 43 = f F 6426keycode 44 = g G 6427keycode 45 = h H 6428keycode 46 = i I 6429keycode 47 = j J 6430keycode 48 = k K 6431keycode 49 = l L 6432keycode 50 = m M 6433keycode 51 = n N 6434keycode 52 = o O 6435keycode 53 = p P 6436keycode 54 = q Q 6437keycode 55 = r R 6438keycode 56 = s S 6439keycode 57 = t T 6440keycode 58 = u U 6441keycode 59 = v V 6442keycode 60 = w W 6443keycode 61 = x X 6444keycode 62 = y Y 6445keycode 63 = z Z 6446keycode 64 = BackSpace 6447keycode 65 = Return 6448keycode 66 = Tab 6449keycode 67 = Escape 6450keycode 68 = Delete 6451END 6452 6453 # 6454 # These are generated using "xmodmap -pke" (and trimmed a little). 6455 # 6456 $linux_xmodmap_pke = <<END; 6457keycode 8 = 6458keycode 9 = Escape 6459keycode 10 = 1 exclam 6460keycode 11 = 2 at 6461keycode 12 = 3 numbersign 6462keycode 13 = 4 dollar 6463keycode 14 = 5 percent 6464keycode 15 = 6 asciicircum 6465keycode 16 = 7 ampersand 6466keycode 17 = 8 asterisk 6467keycode 18 = 9 parenleft 6468keycode 19 = 0 parenright 6469keycode 20 = minus underscore 6470keycode 21 = equal plus 6471keycode 22 = BackSpace Terminate_Server 6472keycode 23 = Tab ISO_Left_Tab 6473keycode 24 = q Q 6474keycode 25 = w W 6475keycode 26 = e E 6476keycode 27 = r R 6477keycode 28 = t T 6478keycode 29 = y Y 6479keycode 30 = u U 6480keycode 31 = i I 6481keycode 32 = o O 6482keycode 33 = p P 6483keycode 34 = bracketleft braceleft 6484keycode 35 = bracketright braceright 6485keycode 36 = Return 6486keycode 37 = Control_L 6487keycode 38 = a A 6488keycode 39 = s S 6489keycode 40 = d D 6490keycode 41 = f F 6491keycode 42 = g G 6492keycode 43 = h H 6493keycode 44 = j J 6494keycode 45 = k K 6495keycode 46 = l L 6496keycode 47 = semicolon colon 6497keycode 48 = apostrophe quotedbl 6498keycode 49 = grave asciitilde 6499keycode 50 = Shift_L 6500keycode 51 = backslash bar 6501keycode 52 = z Z 6502keycode 53 = x X 6503keycode 54 = c C 6504keycode 55 = v V 6505keycode 56 = b B 6506keycode 57 = n N 6507keycode 58 = m M 6508keycode 59 = comma less 6509keycode 60 = period greater 6510keycode 61 = slash question 6511keycode 62 = Shift_R 6512keycode 64 = Alt_L Meta_L 6513keycode 65 = space 6514keycode 94 = less greater 6515END 6516 %Alias = qw(exclam ! at @ dollar $ percent % 6517 asciicircum ^ ampersand & asterisk * minus - underscore _ 6518 equal = plus + bracketleft [ bracketright ] braceleft { 6519 braceright } semicolon ; colon : apostrophe ' quotedbl " 6520 grave ` asciitilde ~ backslash \ bar | less < 6521 period . greater > slash / question ?); 6522 6523 # naughty chatacrers (some of these generate warnings) 6524 @Alias{"parenleft","parenright","space"} = ("(",")"," "); 6525 @Alias{"Tab","Return","numbersign","comma"} = ("\t","\n","#",","); 6526 6527 6528 # 6529 # Populate KeyCode aliase 6530 # 6531 foreach $line (split(/\n/,$sun_xmodmap_pke)) { 6532 ($junk,$code,$junk,$char1,$char2) = split(' ',$line); 6533 if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; } 6534 if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; } 6535 if (length($char1) > 1) { $char1 = "."; } 6536 if (length($char2) > 1) { $char2 = "."; } 6537 $KeyCode{sun}{0}{$code} = $char1; 6538 $KeyCode{sun}{1}{$code} = $char2; 6539 } 6540 foreach $line (split(/\n/,$linux_xmodmap_pke)) { 6541 ($junk,$code,$junk,$char1,$char2) = split(' ',$line); 6542 if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; } 6543 if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; } 6544 if (length($char1) > 1) { $char1 = "."; } 6545 if (length($char2) > 1) { $char2 = "."; } 6546 $KeyCode{linux}{0}{$code} = $char1; 6547 $KeyCode{linux}{1}{$code} = $char2; 6548 } 6549 6550} 6551 6552 6553# Set_VNC_Codes - set globals for VNC. 6554# 6555sub Set_VNC_Codes { 6556 6557 ### set client code to request size hash. 6558 %VNC_Code_Size = ( 0 => 20, 6559 1 => 6, 6560 2 => 4, 6561 3 => 10, 6562 4 => 8, 6563 5 => 6, 6564 6 => 8 ); 6565 6566 ### Some essential keysyms 6567 $KeyCode{vnc}{0}{"\010"} = "\b"; 6568 $KeyCode{vnc}{0}{"\011"} = "\t"; 6569 $KeyCode{vnc}{0}{"\015"} = "\n"; 6570 6571} 6572 6573 6574# JL: Initialize Hostnames for IP addresses 6575sub Set_DNS { 6576 %DNS = ( 6577 "192.168.178.1" => "fritz.box" 6578 ) 6579} 6580 6581# JL: Set_MIME_Types - create hash of MIME types and file extensions. 6582sub Set_MIME_Types { 6583 # Initialize with types seen in the wild but not covered below. 6584 %mime_types = ( 6585 "application/binary" => "binary", 6586 "application/ocsp-response" => "ocsp", 6587 "application/pln" => "pln", # bahn.de schedule 6588 "application/vnd.google.safebrowsing-update" => "safebrowsing-update", 6589 "application/vnd.google.safebrowsing-chunk" => "safebrowsing-chunk", 6590 "application/vnd.ms-sync.wbxml" => "mssync", # MS ActiveSync 6591 "application/x-protobuffer" => "protobuffer", # google 6592 "application/x-smd" => "smd", # MapDroyd boundary 6593 "(application|text)/((x-)?javascript|(x-)?js)" => "js", 6594 "(application|text)/json" => "json", 6595 "application/x-amf" => ".amf", 6596 "application/x-zip-compressed" => ".zip", 6597 "image/bmp" => "bmp", 6598 "image/vnd.microsoft.icon" => "ico", 6599 "image/x-gif" => "gif", 6600 "(image/x-)?jp(e)?g" => "jpeg", 6601 "image/x-png" => "png", 6602 "text/xml" => "xml", 6603 "application/x-bzip2" => "bz2", 6604 "application/x-css" => "css", 6605 "font/woff" => "woff", 6606 "application/font-woff" => "woff"); 6607 # Following created with: 6608 # grep -o -P "^(text|application|audio|image|video).*\t+([a-z0-9]+)" /etc/mime.types > mime.types 6609 $raw_mime_types = <<END; 6610application/andrew-inset ez 6611application/annodex anx 6612application/atom+xml atom 6613application/atomcat+xml atomcat 6614application/atomserv+xml atomsrv 6615application/bbolin lin 6616application/cap cap 6617application/cu-seeme cu 6618application/davmount+xml davmount 6619application/dsptype tsp 6620application/ecmascript es 6621application/futuresplash spl 6622application/hta hta 6623application/java-archive jar 6624application/java-serialized-object ser 6625application/java-vm class 6626application/javascript js 6627application/m3g m3g 6628application/mac-binhex40 hqx 6629application/mac-compactpro cpt 6630application/mathematica nb 6631application/msaccess mdb 6632application/msword doc 6633application/mxf mxf 6634application/octet-stream bin 6635application/oda oda 6636application/ogg ogx 6637application/pdf pdf 6638application/pgp-keys key 6639application/pgp-signature pgp 6640application/pics-rules prf 6641application/postscript ps 6642application/rar rar 6643application/rdf+xml rdf 6644application/rss+xml rss 6645application/rtf rtf 6646application/smil smi 6647application/xhtml+xml xhtml 6648application/xml xml 6649application/xspf+xml xspf 6650application/zip zip 6651application/vnd.android.package-archive apk 6652application/vnd.cinderella cdy 6653application/vnd.google-earth.kml+xml kml 6654application/vnd.google-earth.kmz kmz 6655application/vnd.mozilla.xul+xml xul 6656application/vnd.ms-excel xls 6657application/vnd.ms-pki.seccat cat 6658application/vnd.ms-pki.stl stl 6659application/vnd.ms-powerpoint ppt 6660application/vnd.oasis.opendocument.chart odc 6661application/vnd.oasis.opendocument.database odb 6662application/vnd.oasis.opendocument.formula odf 6663application/vnd.oasis.opendocument.graphics odg 6664application/vnd.oasis.opendocument.graphics-template otg 6665application/vnd.oasis.opendocument.image odi 6666application/vnd.oasis.opendocument.presentation odp 6667application/vnd.oasis.opendocument.presentation-template otp 6668application/vnd.oasis.opendocument.spreadsheet ods 6669application/vnd.oasis.opendocument.spreadsheet-template ots 6670application/vnd.oasis.opendocument.text odt 6671application/vnd.oasis.opendocument.text-master odm 6672application/vnd.oasis.opendocument.text-template ott 6673application/vnd.oasis.opendocument.text-web oth 6674application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx 6675application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx 6676application/vnd.openxmlformats-officedocument.presentationml.presentation pptx 6677application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx 6678application/vnd.openxmlformats-officedocument.presentationml.template potx 6679application/vnd.openxmlformats-officedocument.wordprocessingml.document docx 6680application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx 6681application/vnd.rim.cod cod 6682application/vnd.smaf mmf 6683application/vnd.stardivision.calc sdc 6684application/vnd.stardivision.chart sds 6685application/vnd.stardivision.draw sda 6686application/vnd.stardivision.impress sdd 6687application/vnd.stardivision.math sdf 6688application/vnd.stardivision.writer sdw 6689application/vnd.stardivision.writer-global sgl 6690application/vnd.sun.xml.calc sxc 6691application/vnd.sun.xml.calc.template stc 6692application/vnd.sun.xml.draw sxd 6693application/vnd.sun.xml.draw.template std 6694application/vnd.sun.xml.impress sxi 6695application/vnd.sun.xml.impress.template sti 6696application/vnd.sun.xml.math sxm 6697application/vnd.sun.xml.writer sxw 6698application/vnd.sun.xml.writer.global sxg 6699application/vnd.sun.xml.writer.template stw 6700application/vnd.symbian.install sis 6701application/vnd.visio vsd 6702application/vnd.wap.wbxml wbxml 6703application/vnd.wap.wmlc wmlc 6704application/vnd.wap.wmlscriptc wmlsc 6705application/vnd.wordperfect wpd 6706application/vnd.wordperfect5.1 wp5 6707application/x-123 wk 6708application/x-7z-compressed 7z 6709application/x-abiword abw 6710application/x-apple-diskimage dmg 6711application/x-bcpio bcpio 6712application/x-bittorrent torrent 6713application/x-cab cab 6714application/x-cbr cbr 6715application/x-cbz cbz 6716application/x-cdf cdf 6717application/x-cdlink vcd 6718application/x-chess-pgn pgn 6719application/x-cpio cpio 6720application/x-csh csh 6721application/x-debian-package deb 6722application/x-director dcr 6723application/x-dms dms 6724application/x-doom wad 6725application/x-dvi dvi 6726application/x-httpd-eruby rhtml 6727application/x-font pfa 6728application/x-freemind mm 6729application/x-futuresplash spl 6730application/x-gnumeric gnumeric 6731application/x-go-sgf sgf 6732application/x-graphing-calculator gcf 6733application/x-gtar gtar 6734application/x-hdf hdf 6735application/x-httpd-php phtml 6736application/x-httpd-php-source phps 6737application/x-httpd-php3 php3 6738application/x-httpd-php3-preprocessed php3p 6739application/x-httpd-php4 php4 6740application/x-httpd-php5 php5 6741application/x-ica ica 6742application/x-info info 6743application/x-internet-signup ins 6744application/x-iphone iii 6745application/x-iso9660-image iso 6746application/x-jam jam 6747application/x-java-jnlp-file jnlp 6748application/x-jmol jmz 6749application/x-kchart chrt 6750application/x-killustrator kil 6751application/x-koan skp 6752application/x-kpresenter kpr 6753application/x-kspread ksp 6754application/x-kword kwd 6755application/x-latex latex 6756application/x-lha lha 6757application/x-lyx lyx 6758application/x-lzh lzh 6759application/x-lzx lzx 6760application/x-maker frm 6761application/x-mif mif 6762application/x-ms-wmd wmd 6763application/x-ms-wmz wmz 6764application/x-msdos-program com 6765application/x-msi msi 6766application/x-netcdf nc 6767application/x-ns-proxy-autoconfig pac 6768application/x-nwc nwc 6769application/x-object o 6770application/x-oz-application oza 6771application/x-pkcs7-certreqresp p7r 6772application/x-pkcs7-crl crl 6773application/x-python-code pyc 6774application/x-qgis qgs 6775application/x-quicktimeplayer qtl 6776application/x-redhat-package-manager rpm 6777application/x-ruby rb 6778application/x-sh sh 6779application/x-shar shar 6780application/x-shockwave-flash swf 6781application/x-silverlight scr 6782application/x-stuffit sit 6783application/x-sv4cpio sv4cpio 6784application/x-sv4crc sv4crc 6785application/x-tar tar 6786application/x-tcl tcl 6787application/x-tex-gf gf 6788application/x-tex-pk pk 6789application/x-texinfo texinfo 6790application/x-troff t 6791application/x-troff-man man 6792application/x-troff-me me 6793application/x-troff-ms ms 6794application/x-ustar ustar 6795application/x-wais-source src 6796application/x-wingz wz 6797application/x-x509-ca-cert crt 6798application/x-xcf xcf 6799application/x-xfig fig 6800application/x-xpinstall xpi 6801audio/amr amr 6802audio/amr-wb awb 6803audio/amr amr 6804audio/amr-wb awb 6805audio/annodex axa 6806audio/basic au 6807audio/flac flac 6808audio/midi mid 6809audio/mpeg mpga 6810audio/mpegurl m3u 6811audio/ogg oga 6812audio/prs.sid sid 6813audio/x-aiff aif 6814audio/x-gsm gsm 6815audio/x-mpegurl m3u 6816audio/x-ms-wma wma 6817audio/x-ms-wax wax 6818audio/x-pn-realaudio ra 6819audio/x-realaudio ra 6820audio/x-scpls pls 6821audio/x-sd2 sd2 6822audio/x-wav wav 6823image/gif gif 6824image/ief ief 6825image/jpeg jpeg 6826image/pcx pcx 6827image/png png 6828image/svg+xml svg 6829image/tiff tiff 6830image/vnd.djvu djvu 6831image/vnd.wap.wbmp wbmp 6832image/x-canon-cr2 cr2 6833image/x-canon-crw crw 6834image/x-cmu-raster ras 6835image/x-coreldraw cdr 6836image/x-coreldrawpattern pat 6837image/x-coreldrawtemplate cdt 6838image/x-corelphotopaint cpt 6839image/x-epson-erf erf 6840image/x-icon ico 6841image/x-jg art 6842image/x-jng jng 6843image/x-ms-bmp bmp 6844image/x-nikon-nef nef 6845image/x-olympus-orf orf 6846image/x-photoshop psd 6847image/x-portable-anymap pnm 6848image/x-portable-bitmap pbm 6849image/x-portable-graymap pgm 6850image/x-portable-pixmap ppm 6851image/x-rgb rgb 6852image/x-xbitmap xbm 6853image/x-xpixmap xpm 6854image/x-xwindowdump xwd 6855text/cache-manifest manifest 6856text/calendar ics 6857text/css css 6858text/csv csv 6859text/h323 323 6860text/html html 6861text/iuls uls 6862text/mathml mml 6863text/plain asc 6864text/richtext rtx 6865text/scriptlet sct 6866text/texmacs tm 6867text/tab-separated-values tsv 6868text/vnd.sun.j2me.app-descriptor jad 6869text/vnd.wap.wml wml 6870text/vnd.wap.wmlscript wmls 6871text/x-bibtex bib 6872text/x-boo boo 6873text/x-c++hdr h 6874text/x-c++src c 6875text/x-chdr h 6876text/x-component htc 6877text/x-csh csh 6878text/x-csrc c 6879text/x-dsrc d 6880text/x-diff diff 6881text/x-haskell hs 6882text/x-java java 6883text/x-literate-haskell lhs 6884text/x-moc moc 6885text/x-pascal p 6886text/x-pcs-gcd gcd 6887text/x-perl pl 6888text/x-python py 6889text/x-scala scala 6890text/x-setext etx 6891text/x-sh sh 6892text/x-tcl tcl 6893text/x-tex tex 6894text/x-vcalendar vcs 6895text/x-vcard vcf 6896video/3gpp 3gp 6897video/annodex axv 6898video/dl dl 6899video/dv dif 6900video/fli fli 6901video/gl gl 6902video/mpeg mpeg 6903video/mp4 mp4 6904video/quicktime qt 6905video/ogg ogv 6906video/vnd.mpegurl mxu 6907video/x-flv flv 6908video/x-la-asf lsf 6909video/x-mng mng 6910video/x-ms-asf asf 6911video/x-ms-wm wm 6912video/x-ms-wmv wmv 6913video/x-ms-wmx wmx 6914video/x-ms-wvx wvx 6915video/x-msvideo avi 6916video/x-sgi-movie movie 6917video/x-matroska mpv 6918END 6919 %ext_types = (); 6920 foreach $line (split(/\n/, $raw_mime_types)) { 6921 my ($mime_type, $extension) = split(/\t+/, $line); 6922 # Beware. "+" needs to be escaped in patterns. 6923 $mime_type =~ s/\+/\\\+/g; 6924 $mime_types{$mime_type} = $extension; 6925 my ($type, $sub_type) = split(/\//, $mime_type); 6926 # We check whether we already have a value for this extension. 6927 # We don't care if application is overwritten. 6928 if ((defined $ext_types{$extension}) && 6929 ($ext_types{$extension} ne $type) && 6930 ($ext_types{$extension} ne "application")) { 6931 print "$extension already has $ext_types{$extension}."; 6932 print "Should now become $type. Fix mime.types?\n" 6933 } 6934 else { 6935 $ext_types{$extension} = $type; 6936 } 6937 } 6938} 6939 6940 6941 6942# Touch_Vars - This is stops perl -w warnings about vars used only once. 6943# Part of my todo list is to cull this list. 6944# 6945# 6946sub Touch_Vars { 6947 # 6948 # Perl < 5.6 code 6949 # 6950 #use vars qw($ip_ttl $udp_checksum $ip_ident $tcp_length_data 6951 #$ip_tos $tcp_options $opt_A $opt_D $tcp_header_rest $opt_J 6952 #$opt_P $opt_U $opt_X $opt_e $opt_h $opt_i $pad $opt_j 6953 #$snoop_length_orig $http_header $opt_p $opt_q $opt_r 6954 #$header_rest $tcp_ack $ether_dest $ether_src $skip 6955 #$ip_length $udp_length $ip_options $ip_checksum 6956 #$opt_b $opt_B $opt_l $opt_L $ip_rest $ip_hop $ip_reserved 6957 #$ip_flow $icmp_rest $opt_f $opt_z); 6958 # 6959 # Perl 5.6 code 6960 # 6961 #our ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data, 6962 #$ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J, 6963 #$opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j, 6964 #$snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r, 6965 #$header_rest,$tcp_ack,$ether_dest,$ether_src,$skip, 6966 #$ip_length,$udp_length,$ip_options,$ip_checksum, 6967 #$opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved, 6968 #$ip_flow,$icmp_rest,$opt_f,$opt_z); 6969 # 6970 # Perl < 5.6 and 5.6 code (but not elegant) 6971 # 6972 @Once_is_okay = ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data, 6973 $ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J, 6974 $opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j, 6975 $snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r, 6976 $header_rest,$tcp_ack,$ether_dest,$ether_src,$skip, 6977 $ip_length,$udp_length,$ip_options,$ip_checksum,$tcp_rst,$tcp_fin, 6978 $opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved, 6979 $ip_flow,$icmp_rest,$opt_f,$opt_z,$junk1,$opt_H,$opt_I,$opt_R); 6980} 6981 6982 6983# Check_Command - check which is the network sniffing command and save 6984# it to $command. 6985# 6986sub Check_Command { 6987 6988 # 6989 # Check which OS we are on, die if it looks incompatible 6990 # 6991 if ($^O eq "linux") { 6992 # 6993 # The "-s9999" tells tcpdump to keep a packet up to this 6994 # size, otherwise the default is 68 bytes. Some versions of 6995 # tcpdump allow using "-s0" for unlimited. 6996 # 6997 $command = "tcpdump -s9999 -w"; 6998 } elsif ($^O eq "solaris") { 6999 $command = "snoop -o"; 7000 } elsif ($^O eq "darwin") { 7001 $command = "tcpdump -s0 -w"; 7002 } else { 7003 die "ERROR54: Can't find the sniffer command for \"$^O\".\n" . 7004 "\t Please use log mode instead.\n"; 7005 } 7006 7007 # 7008 # Check username 7009 # 7010 if ($ENV{LOGNAME} ne "root") { 7011 print STDERR "WARNING: Are you root? If not, this probably " 7012 . "won't work. Trying anyway...\n"; 7013 } 7014} 7015 7016 7017# Process_Command_Line_Arguments - this process the command line arguments 7018# and sets various globals which are kept in %Arg. It also prints 7019# usage and exists if need be. 7020# 7021sub Process_Command_Line_Arguments { 7022 my $result; 7023 7024 # 7025 # Process Global Defaults into %Arg 7026 # 7027 foreach (@Save_As_HTML_TCP_Ports) { 7028 $Arg{Save_As_TCP_HTML}{$_} = 1; 7029 } 7030 foreach (@Save_As_HTML_UDP_Ports) { 7031 $Arg{Save_As_UDP_HTML}{$_} = 1; 7032 } 7033 foreach (@Save_As_TCP_Playback_Ports) { 7034 $Arg{Save_As_TCP_Playback}{$_} = 1; 7035 } 7036 foreach (@Save_As_UDP_Playback_Ports) { 7037 $Arg{Save_As_UDP_Playback}{$_} = 1; 7038 } 7039 foreach (@Save_As_X11_Playback_Ports) { 7040 $Arg{Save_As_X11_Playback}{$_} = 1; 7041 } 7042 foreach (@Save_As_HTML_X11_Ports) { 7043 $Arg{Save_As_X11_HTML}{$_} = 1; 7044 } 7045 foreach (@Save_As_VNC_Playback_Ports) { 7046 $Arg{Save_As_VNC_Playback}{$_} = 1; 7047 } 7048 7049 if (defined $ARGV[0]) { 7050 ### Dump full help if asked 7051 &Usage_Full if $ARGV[0] eq "--help"; 7052 7053 ### Dump massive help if asked 7054 &Usage_Massive if $ARGV[0] eq "--help2"; 7055 } 7056 7057 # 7058 # Command Line Defaults 7059 # 7060 $Arg{output_raw} = 0; 7061 $Arg{output_hex} = 0; 7062 $Arg{output_UDP} = 1; 7063 $Arg{output_TCP} = 1; 7064 $Arg{output_ICMP} = 1; 7065 $Arg{output_info} = 0; 7066 $Arg{output_apps} = 1; 7067 $Arg{output_index} = 1; 7068 $Arg{prefer_dns} = 0; # JL: Prefer DNS names over IP addresses? 7069 $Arg{httplog_html} = 0; # JL: Should we create HTTPlog in HTML? 7070 $Arg{httplog_name} = "httplog.text"; # JL: Old default as variable 7071 $Arg{httplog_txt} = "httplog.txt"; # JL: New text format 7072 $Arg{keydata} = 0; 7073 $Arg{debug} = 0; 7074 7075 # 7076 # Check correct switches were used 7077 # 7078 Getopt::Long::Configure ("bundling"); 7079 $result = GetOptions ( 7080 "application!" => \$opt_a, 7081 "a" => \$opt_a, 7082 "d|preferdns" => \$opt_d, # JL: new option 7083 "e|everything" => \$opt_e, 7084 "h" => \$opt_h, 7085 "info!" => \$opt_i, 7086 "i" => \$opt_i, 7087 "n|names" => \$opt_n, # JL: new option 7088 "q|quiet" => \$opt_q, 7089 "raw!" => \$opt_r, 7090 "r" => \$opt_r, 7091 "v|verbose" => \$opt_v, 7092 "index!" => \$opt_x, 7093 "x" => \$opt_x, 7094 "A" => \$opt_A, 7095 "H|hex" => \$opt_H, 7096 "I" => \$opt_I, 7097 "R" => \$opt_R, 7098 "U|noudp" => \$opt_U, 7099 "T|notcp" => \$opt_T, 7100 "Y|noicmp" => \$opt_Y, 7101 "X" => \$opt_X, 7102 "D|dir=s" => \$opt_D, 7103 "b|playtcp=s" => \$opt_b, 7104 "B|playudp=s" => \$opt_B, 7105 "l|htmltcp=s" => \$opt_l, 7106 "L|htmludp=s" => \$opt_L, 7107 "m|min=s" => \$opt_m, 7108 "M|max=s" => \$opt_M, 7109 "o|sort=s" => \$opt_o, 7110 "p|port=s" => \$opt_p, 7111 "P|noport=s" => \$opt_P, 7112 "j|ipaddr=s" => \$opt_j, 7113 "J|noipaddr=s" => \$opt_J, 7114 "s|runonce=s" => \$opt_s, 7115 "S|runmany=s" => \$opt_S, 7116 "z|runredo" => \$opt_z, 7117 "f|filter=s" => \$opt_f, 7118 "k|keydata" => \$opt_k, 7119 "debug" => \$opt_debug, 7120 "bench" => \$opt_bench 7121 ); 7122 7123 # 7124 # Process switches 7125 # 7126 &Usage() if ($opt_h || ! $result); 7127 $Arg{output_raw} = 1 if $opt_r or $opt_v; 7128 $Arg{output_hex} = 1 if $opt_H or $opt_e; 7129 $Arg{output_info} = 1 if $opt_i or $opt_v; 7130 $Arg{quiet} = 1 if $opt_q; 7131 $Arg{output_UDP} = 0 if $opt_U; 7132 $Arg{output_TCP} = 0 if $opt_T; 7133 $Arg{output_ICMP} = 0 if $opt_Y; 7134 $Arg{output_apps} = 0 if ($opt_A || (defined $opt_a && $opt_a eq "0")); 7135 $Arg{output_index} = 0 if ($opt_X || (defined $opt_x && $opt_x eq "0")); 7136 $Arg{output_allhtml} = 1 if $opt_e; 7137 $Arg{prefer_dns} = 1 if $opt_d; 7138 $Arg{httplog_html} = 1 if $opt_n; 7139 $Arg{httplog_name} = "httplog.html" if $opt_n; 7140 my $extra_TCPplayback = $opt_b; 7141 my $extra_UDPplayback = $opt_B; 7142 my $extra_TCPhtml = $opt_l; 7143 my $extra_UDPhtml = $opt_L; 7144 my $ports_accepted = $opt_p; 7145 my $ports_rejected = $opt_P; 7146 my $ips_accepted = $opt_j; 7147 my $ips_rejected = $opt_J; 7148 $Arg{output_dir} = $opt_D; 7149 $Arg{filter} = $opt_f || ""; 7150 $Arg{minbytes} = 0; 7151 $Arg{maxbytes} = 0; 7152 $Arg{sort} = "time"; 7153 $Arg{keydata} = 1 if $opt_k; 7154 $Arg{debug} = 1 if $opt_debug; 7155 $Arg{bench} = 1 if $opt_bench; 7156 7157 mkdir $Arg{output_dir}; 7158 7159 # 7160 # Check for min/max bytes 7161 # 7162 if (defined $opt_m) { 7163 if ($opt_m =~ /k$/) { 7164 $opt_m =~ s/k$//; 7165 $opt_m *= 1024; 7166 } 7167 $Arg{minbytes} = $opt_m; 7168 } 7169 if (defined $opt_M) { 7170 if ($opt_M =~ /k$/) { 7171 $opt_M =~ s/k$//; 7172 $opt_M *= 1024; 7173 } 7174 $Arg{maxbytes} = $opt_M; 7175 } 7176 7177 # 7178 # Check for sort option 7179 # 7180 if (defined $opt_o) { 7181 if ($opt_o !~ /^(time|size|type|ip)$/) { 7182 print STDERR "ERROR55: Sort must be \"time\", " . 7183 "\"size\", \"type\" or \"ip\".\n"; 7184 &Usage(); 7185 } 7186 $Arg{sort} = $opt_o; 7187 } 7188 7189 # 7190 # Check for standalone redo mode 7191 # 7192 if (defined $opt_z) { 7193 $Arg{redo} = 1; 7194 if (defined $Arg{output_dir}) { 7195 # bad luck 7196 die "ERROR56: Can't use an output dir " 7197 . "$Arg{output_dir} in redo mode.\n\n"; 7198 } 7199 } 7200 7201 # 7202 # Check for standalone mode 7203 # 7204 elsif (defined $opt_s || defined $opt_S) { 7205 $Arg{standalone} = 1; 7206 if (defined $opt_s) { 7207 if ($opt_s =~ /,/) { 7208 die "ERROR57: Unexpected comma found in " . 7209 "\"-s$opt_s\" (did you mean \"-S$opt_s\"?)\n"; 7210 } 7211 $Arg{mins} = $opt_s; 7212 $Arg{count} = 1; 7213 } elsif (defined $opt_S) { 7214 my ($mins,$count) = split(/,/,$opt_S); 7215 $Arg{mins} = $mins; 7216 ### -1 means endless 7217 $Arg{count} = $count || -1; 7218 } 7219 } 7220 7221 # 7222 # This is normal mode 7223 # 7224 else { 7225 $Arg{normal} = 1; 7226 } 7227 7228 # 7229 # Build accepted or rejected port list as %Arg{Port_Accepted},... 7230 # 7231 if (defined $ports_accepted) { 7232 $Arg{port_accept} = 1; 7233 foreach $port (split(/,/,$ports_accepted)) { 7234 $Arg{Port_Accepted}{$port} = 1; 7235 } 7236 } 7237 if (defined $ports_rejected) { 7238 $Arg{port_reject} = 1; 7239 foreach $port (split(/,/,$ports_rejected)) { 7240 $Arg{Port_Rejected}{$port} = 1; 7241 } 7242 } 7243 7244 # 7245 # Build accepted or rejected IP list as %Arg{IP_Accepted},... 7246 # 7247 if (defined $ips_accepted) { 7248 $Arg{ip_accept} = 1; 7249 foreach $ip (split(/,/,$ips_accepted)) { 7250 $Arg{IP_Accepted}{$ip} = 1; 7251 } 7252 } 7253 if (defined $ips_rejected) { 7254 $Arg{ip_reject} = 1; 7255 foreach $ip (split(/,/,$ips_rejected)) { 7256 $Arg{IP_Rejected}{$ip} = 1; 7257 } 7258 } 7259 7260 # 7261 # Add extra ports to playback or HTML 7262 # 7263 if (defined $extra_TCPplayback) { 7264 foreach $port (split(/,/,$extra_TCPplayback)) { 7265 $Arg{Save_As_TCP_Playback}{$port} = 1; 7266 } 7267 } 7268 if (defined $extra_UDPplayback) { 7269 foreach $port (split(/,/,$extra_UDPplayback)) { 7270 $Arg{Save_As_UDP_Playback}{$port} = 1; 7271 } 7272 } 7273 if (defined $extra_TCPhtml) { 7274 foreach $port (split(/,/,$extra_TCPhtml)) { 7275 $Arg{Save_As_TCP_HTML}{$port} = 1; 7276 } 7277 } 7278 if (defined $extra_UDPhtml) { 7279 foreach $port (split(/,/,$extra_UDPhtml)) { 7280 $Arg{Save_As_UDP_HTML}{$port} = 1; 7281 } 7282 } 7283 7284 # 7285 # Check infile was provided, or print usage 7286 # 7287 if (! defined $ARGV[0] && ! ($Arg{standalone} || $Arg{redo})) { 7288 &Usage(); 7289 } 7290 @{$Arg{infiles}} = @ARGV; 7291} 7292 7293 7294# Usage - print command usage and exit. 7295# 7296sub Usage { 7297 print "USAGE: chaosreader [-adehiknqrvxAEHIRTUXY] [-D dir] 7298 [-b port[,...]] [-B port[,...]] 7299 [-j IPaddr[,...]] [-J IPaddr[,...]] 7300 [-l port[,...]] [-L port[,...]] [-m bytes[k]] 7301 [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"] 7302 [-p port[,...]] [-P port[,...]] 7303 infile [infile2 ...] 7304 chaosreader -s [mins] | -S [mins[,count]] 7305 [-z] [-f 'filter'] 7306 eg, chaosreader infile # Create application session files, indexes 7307 chaosreader -v infile # Verbose - Create ALL files 7308 chaosreader -i infile # Create info files 7309 chaosreader -r infile # Create raw files 7310 chaosreader -S 2,5 # Standalone - sniff network 5 times by 2 mins. 7311 chaosreader -h # Print a brief help (this) 7312 chaosreader --help # Print verbose help and version 7313 chaosreader --help2 # Print massive help\n\n"; 7314 exit(0); 7315} 7316 7317 7318# Usage Full - print command usage and exit. 7319# 7320sub Usage_Full { 7321 print "Version 0.95i, 14-Apr-2014 7322 7323USAGE: chaosreader [-adehiknqrvxAHIRTUXY] [-D dir] 7324 [-b port[,...]] [-B port[,...]] 7325 [-j IPaddr[,...]] [-J IPaddr[,...]] 7326 [-l port[,...]] [-L port[,...]] [-m bytes[k]] 7327 [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"] 7328 [-p port[,...]] [-P port[,...]] 7329 infile [infile2 ...] 7330 7331 chaosreader -s [mins] | -S [mins[,count]] 7332 [-z] [-f 'filter'] 7333 7334 chaosreader # Create application session files, indexes 7335 7336 -a, --application # Create application session files (default) 7337 -d, --preferdns # Show DNS names instead of IP addresses 7338 -e, --everything # Create HTML 2-way & hex files for everything 7339 -h # Print a brief help 7340 --help # Print verbose help (this) and version 7341 --help2 # Print massive help 7342 -i, --info # Create info file 7343 -q, --quiet # Quiet, no output to screen 7344 -r, --raw # Create raw files 7345 -v, --verbose # Verbose - Create ALL files .. (except -e) 7346 -x, --index # Create index files (default) 7347 -A, --noapplication # Exclude application session files 7348 -H, --hex # Include hex dumps (slow) 7349 -I, --noinfo # Exclude info files 7350 -R, --noraw # Exclude raw files 7351 -T, --notcp # Exclude TCP traffic 7352 -U, --noudp # Exclude UDP traffic 7353 -Y, --noicmp # Exclude ICMP traffic 7354 -X, --noindex # Exclude index files 7355 -k, --keydata # Create extra files for keystroke analysis 7356 -n, --names # Include hostnames in hyperlinked HTTPlog (HTML) 7357 -D dir --dir dir # Output all files to this directory 7358 -b 25,79 --playtcp 25,79 # replay these TCP ports as well (playback) 7359 -B 36,42 --playudp 36,42 # replay these UDP ports as well (playback) 7360 -l 7,79 --htmltcp 7,79 # Create HTML for these TCP ports as well 7361 -L 7,123 --htmludp 7,123 # Create HTML for these UDP ports as well 7362 -m 1k --min 1k # Min size of connection to save (\"k\" for Kb) 7363 -M 1024k --max 1k # Max size of connection to save (\"k\" for Kb) 7364 -o size --sort size # sort Order: time/size/type/ip (Default time) 7365 -p 21,23 --port 21,23 # Only examine these ports (TCP & UDP) 7366 -P 80,81 --noport 80,81 # Exclude these ports (TCP & UDP) 7367 -s 5 --runonce 5 # Standalone. Run tcpdump/snoop for 5 mins. 7368 -S 5,10 --runmany 5,10 # Standalone, many. 10 samples of 5 mins each. 7369 -S 5 --runmany 5 # Standalone, endless. 5 min samples forever. 7370 -z --runredo # Standalone, redo. Rereads last run's logs. 7371 -j 10.1.2.1 --ipaddr 10.1.2.1 # Only examine these IPs 7372 -J 10.1.2.1 --noipaddr 10.1.2.1 # Exclude these IPs 7373 -f 'port 7' --filter 'port 7' # With standalone, use this dump filter. 7374 7375eg1, 7376 tcpdump -s9000 -w output1 # create tcpdump capture file 7377 chaosreader output1 # extract recognised sessions, or, 7378 chaosreader -ve output1 # gimme everything, or, 7379 chaosreader -p 20,21,23 output1 # only ftp and telnet... 7380eg2, 7381 snoop -o output1 # create snoop capture file instead 7382 chaosreader output1 # extract recognised sessions... 7383eg3, 7384 chaosreader -S 2,5 # Standalone, sniff network 5 times for 2 mins 7385 # each. View index.html for progress (or .text) 7386"; 7387 exit(0); 7388} 7389 7390 7391# Usage_Massive - print massive help. Actually strip it from the comments 7392# at the top of the code. 7393# 7394sub Usage_Massive { 7395 open (MYSELF,"$0") || die "ERROR58: I can't see myself: $!\n"; 7396 @Myself = <MYSELF>; 7397 close MYSELF; 7398 7399 ### Print comment from top of code 7400 foreach $line (@Myself) { 7401 last if $line !~ /^#/; 7402 last if $line =~ /^# Todo:/; 7403 next if $line =~ m:^#!/usr/bin/perl:; 7404 $line =~ s/^#/ /; 7405 print $line; 7406 } 7407 print "\n"; 7408 7409 exit(0); 7410} 7411 7412 7413 7414__END__ 7415 7416Reminders for myself 7417==================== 7418/s for multiline match 7419 7420 7421Comments style: 7422 7423# Micro comment 7424 7425### Tiny Comment 7426 7427# 7428# Small comment 7429# 7430 7431# 7432# --- Meduim Comment --- 7433# 7434 7435######################### 7436# --- Large Comment --- 7437# 7438 7439######################## 7440# --- Huge Comment --- # 7441######################## 7442 7443 7444Error message style 7445=================== 7446 7447die "ERROR#: message: $!\n"; 7448 7449 7450 7451Data types, 7452=========== 7453 %Arg 7454 -> @infiles 7455 -> output_raw 7456 -> output_hex 7457 -> output_UDP 7458 -> output_info 7459 -> output_apps 7460 -> output_index 7461 -> output_allhtml 7462 -> Save_As_TCP_HTML 7463 -> $port 7464 -> Save_As_UDP_HTML 7465 -> $port 7466 -> Save_As_TCP_Playback 7467 -> $port 7468 -> Save_As_UDP_Playback 7469 -> $port 7470 -> Port_Accepted 7471 -> $port 7472 -> Port_Rejected 7473 -> $port 7474 -> ip_accept 7475 -> ip_reject 7476 -> IP_Accepted 7477 -> $ip 7478 -> IP_Rejected 7479 -> $ip 7480 -> debug 7481 -> standalone 7482 -> redo 7483 -> normal 7484 -> mins 7485 -> count 7486 -> output_dir 7487 -> quiet 7488 -> infile 7489 -> minbytes 7490 -> maxbytes 7491 7492 %IP 7493 -> time 7494 -> $packet_time 7495 -> ver 7496 -> src 7497 -> dest 7498 -> protocol 7499 -> frag 7500 -> $ip_frag 7501 -> fragged 7502 -> drops 7503 -> id 7504 -> $ip_id 7505 -> StartTime 7506 7507 %TCP 7508 -> id 7509 -> $session_id 7510 -> src 7511 -> dest 7512 -> source # SYN seen 7513 -> src_port 7514 -> dest_port 7515 -> Aseq 7516 -> $$tcp_seq 7517 -> Bseq 7518 -> $$tcp_seq 7519 -> time 7520 -> $time 7521 -> dir 7522 -> data 7523 -> BothHTML 7524 -> StartTime 7525 -> EndTime 7526 -> size 7527 -> knowndir 7528 7529 %UDP 7530 -> id 7531 -> $session_id 7532 -> src 7533 -> dest 7534 -> src_port 7535 -> dest_port 7536 -> RawA 7537 -> RawB 7538 -> time 7539 -> $time 7540 -> BothHTML 7541 -> StartTime 7542 -> EndTime 7543 -> size 7544 7545 %ICMP 7546 -> time 7547 -> type 7548 -> code 7549 -> src 7550 -> dest 7551 -> Partial 7552 -> ver 7553 -> size 7554 7555 %Count 7556 -> IP 7557 -> IPprotocols 7558 -> TCPports 7559 -> UDPports 7560 -> EtherType 7561 7562 %CountMaster 7563 (as above) 7564 7565 %Index 7566 -> @HTML 7567 -> @Text 7568 -> Time_Order 7569 -> $session_timeid 7570 -> Sort_Lookup 7571 -> $session_timeid 7572 7573 %Image 7574 -> @HTML 7575 -> links 7576 -> info 7577 -> notempty 7578 7579 %GETPOST 7580 -> @HTML 7581 -> query 7582 -> info 7583 -> notempty 7584 7585 %Hex 7586 -> $type 7587 -> $session_id 7588 -> [ $from_server, data ] 7589 %Filenames 7590 -> $time 7591 -> filename 7592 -> service 7593 -> session_id 7594 7595 @Master 7596 -> starttime 7597 -> endtime 7598 -> duration 7599 -> size 7600 -> dir 7601 -> file 7602