用PERL实现一个简单的NIDS FTP.pm FTP协议解码模块,抽取数据包里的FTP命令及相应的参数,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/ -------- 8< ------------ # # NetPacket::FTP - Decode FTP packets # # Comments/suggestions to stardust at xfocus dot org # # # $Id: FTP.pm,v 1.16 2004/03/03 l1:16:20 stardust Exp $ # package NetPacket::FTP; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); use NetPacket; my $myclass; BEGIN { $myclass = __PACKAGE__; $VERSION = "0.01"; } sub Version () { "$myclass v$VERSION" } BEGIN { @ISA = qw(Exporter NetPacket); # Items to export into callers namespace by default # (move infrequently used names to @EXPORT_OK below) @EXPORT = qw( ); # Other items we are prepared to export if requested @EXPORT_OK = qw( ); # Tags: %EXPORT_TAGS = ( ALL => [@EXPORT, @EXPORT_OK], ); } # # Decode the packet # # FTP协议文本参看RFC959,http://www.ietf.org/rfc/rfc0959.txt # 常见的FTP命令 my @ftp_cmds = qw(ABOR ACCT ALLO APPE CDUP CWD DELE HELP LIST MKD MODE NLST NOOP PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR RNTO SITE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD XPWD XRMD LPRT LPSV ADAT AUTH CCC CONF ENC MIC PBSZ PROT FEAT OPTS EPRT EPSV LANG MDTM MLSD MLST SIZE DIGT CLNT MACB ); sub decode { my $class = shift; my($data) = @_; my $self = {}; my $cmdhead = 0; my $cmdtail = 0; my @parts = (); my $cmdcount = 0; my $returnindex = 0; my $data_len = length($data); # 如果数据长度过短则不处理 if ($data_len >= 4) { # 一个包里的FTP命令个数 $self->{cmdcount} = 0; # 搜索回车,之前认为是一个命令行,需要注意的是一个包里可能包含多个FTP命令 while ( (($returnindex = index ($data,"\x0a",$cmdhead)) >=0) (($returnindex < 0) && (($data_len - $cmdhead) >= 4))) { # 调整一个命令行串尾指针 if ($returnindex < 0) { $cmdtail = $data_len -1; } else { $cmdtail = $returnindex; } if ((my $cmdlen = ($cmdtail - $cmdhead + 1)) >= 4) { # 取出命令行串 my $cmdline = substr($data,$cmdhead,$cmdlen); # 从命令行里拆分出命令名和它的参数串 if (splitcmd($cmdline,\@parts)) { $self->{cmdcount}++; my $cmdindex = "cmd"."$self->{cmdcount}"; my $paraindex = "para"."$self->{cmdcount}"; # 记录到要返回到主程序的对象 $self->{$cmdindex} = $parts[0]; $self->{$paraindex} = $parts[1]; } } # 调整命令行串头指针 $cmdhead = $cmdtail + 1; } # 如果命令个数大于0,则说明解码是有效的 if ($self->{cmdcount} == 0) { $self->{valid} = 0; } else { $self->{valid} = 1; } } else { $self->{valid} = 0; } # 返回对象 bless($self, $class); return $self; } sub splitcmd { my ($cmdline,$parts) = @_; # 去除行尾的回车 chomp($cmdline); # 用正则表达式抽取出命令名字和参数,既然效率不是考虑的主要问题就“毫无顾忌”地使用正则表达式,因为方便 if ($cmdline =~ m/^\s*([a-zA-Z]{3,4})\s+(.*)/) { my $valid_cmd = 0; # 检查抽出来的命令名字是否是一个已知的合法FTP命令 for (my $i=0;$i<@ftp_cmds;$i++) { if ($ftp_cmds[$i] eq uc($1)) { $valid_cmd = 1; last; } } # 如果是合法的命令则返回给调用函数 if ($valid_cmd) { ${$parts}[0] = $1; ${$parts}[1] = $2; return 1; } else { return 0; } } else { return 0; } } # # Module initialisation # 1; # autoloaded methods go after the END token (&& pod) below __END__ ------------- 8< ------------ |