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