1.5 Perl
Perl是Lary Wall于1987年创建并发布给大多数Usenet新闻组服务器的脚本语言,它是由流行的基于UNIX的脚本语言引擎的特性和功能组合成的核心语言。sh、sed和awk等特性与正则表达式的结合,以及WWW诞生之后的Internet计算的广泛传播,使得Perl获得了巨大的成功,并迅速被脚本界采用。
CGI应用程序通常用于向Web用户提供动态数据、数据库访问以及和其他应用程序交互的通用数据格式和机制。由于Perl被视为最简单的创建CGI应用程序的方法,因此可以说Perl的发展贯穿了WWW的整个扩展过程。Perl以正则表达式的灵活性和实现著称,通常被认为是功能最大的正则表达式引擎。使用Perl的正则表达式引擎,只需要一行Perl代码就可以创建模式匹配和字符串替代算法;而如果用C语言实现这些算法的话,可能要花费几百行的代码才能完成。例如,下面的正则表达式会搜索给定的字符串,并使用字符串“dog”取代字符串“cat”。
Expression:
$mystring =~ s/cat/dog/;
如果用C语言完成一个循环结构的算法以实现数据读取、字符处理、字符串替代等会很困难,而且其开发过程会很长,从而使信息安全程序员、系统管理员、学生和黑客出于各种不同目的不约而同地使用Perl,如:基于Web的商业应用、任务管理工具包、生物工程的复杂对象和类、简单的CGI Web页面计数器和安全工具等。除了大量用于调整远程和本地漏洞的exploit外,使用Perl引擎的流行的安全工具还包括Whisker、Narrow Security Scanner和Wellenreiter等。
Perl是大多数安全团体都采用的脚本语言,这归功于Perl的平台独立性、简单的套接字结构、利用二进制编码的能力和广泛流行等特性。GNU Perl和ActiveState Win32 Perl的解释器都可用于Microsoft 95/98/ME/NT/2000/XP/.NET、Solaris、NetBSD/ OpenBSD/ FreeBSD、Irix、HPUX、Red Hat和其他Linux平台。
1.5.1 数据类型
一般来说,Perl中的数据声明非常简单,通常可以指定为以下3种数据类型之一:标量、数组和哈希。和大多树结构化语言不同,Perl使用自动值测定,以同种方式来处理字符、字符串和数字。所有的标量以$字符为前缀。例如,将5赋值给变量Gabe要写成"$Gabe=5;"。另外还要注意,Perl的变量不必进行初始化,可以在任何条件下定义一个变量的值。数组(也被称为列表数组)无论是动态还是静态的,都以@开始,并携带字符、数字和字符串列表。另外,Perl还有利用数组的数组的功能。例1-25创建了一个由8个数据字段组成的多维静态数组。
例1-25 用Perl创建一个含8个数据字段的多维数组
1 @ArrayOfArray = (
2 [ "foster", "price" ],
3 [ "anthony", "marshall", "chad" ],
4 [ "tom", "eric", "gabe" ],
5 );
6 print $ArrayOfArray[2][2];
因为数组的定位是从[0][0]而不是从[1][1]开始,所以上述代码的结果是打印出gabe,而不是marshall。
哈希(或称关联数组)允许用户用关联字符串来取代静态数组列表,来访问数组中的实体。如例1-26所示,用户可以使用数组列表将与实体对应的字符串和数字保存到同一个数组中,而不需要考虑层次顺序来定位每个元素。
例1-26 数组列表
1 %jobs = ("Coder", 21,
2 "Programmer", 24,
3 "Developer", 27);
后继的表达式就可以用字符串引用来查找对应的数组实体,而无须采用静态整数来定位。对于非常大型的数组而言,采用哈希表来管理和获取数据,要比单纯使用一维数组或非哈希结构的数组要方便得多。例1-27通过指定字符串来检索关联数组的数据。第1行返回27,第2行返回24,第3行返回21。
例1-27 指定字符串检索相关数组的数据
1 $jobs{"Developer"};
2 $jobs{"Programmer"};
3 $jobs{"Coder"};
Perl可以实现列表数组和哈希表之间的转换。这在检索完整信息集或系统评价数组中的每个项目的时候非常有用。在下面的代码中,第1行将原始的哈希表转换成列表数组,而第3行将列表数组转换成哈希表。第2行引用前面定义的数组中的第3个元素并返回24。
1 @staticjobs = %jobs;
2 $staticjobs[3];
3 %jobscopy = @staticjobs;
在前面的示例中,要注意%、@和$指定不同数据类型的用法。用正确的对应的前缀来引用数据类型是很重要的。
1.5.2 运算符
Perl有以下5种运算符:算术运算符、赋值运算符、逻辑运算符、关系运算符和字符串运算符。这些运算符通常用于初始化、定义、关联、计算或修改需要的表达式或变量数据。表1-1定义了Perl的算术运算符。
表1-1 Perl算术运算符
|
运 算 符 |
描 述 |
例 子 |
|
+ |
返回两个变量的和 |
$education + $experience |
|
– |
返回两个变量的差 |
$education–$experience |
|
* |
返回两个变量的乘积 |
$num1 * $num2 |
|
/ |
返回两个变量的商 |
$num1 / $num2 |
|
% |
返回两个变量的模或余数 |
$num1 % $num2 |
|
** |
返回两个变量的幂 |
$num1 ** $num2 |
赋值运算符用于定义和操作标量变量,而不是数组。赋值运算符和算术运算符的最小差别就是,赋值运算符可以在一个独立的表达式中为同一个标量重新分配值。如果number的值为5,那么执行表达式“$number=+2;”后其值就会变为7。表1-2总结了所有的赋值运算符。
表1-2 Perl赋值运算符
|
运 算 符 |
描 述 |
例 子 |
|
|
= |
给变量赋值 |
$num1 = 10 $gabe = red |
|
|
++ |
以1为步长增加变量的值 |
$num1++ ++$num1 |
|
|
–– |
以1为步长减少变量的值 |
$num1–– ––$num1 |
|
|
运 算 符 |
描 述 |
例 子 |
|
|
+= |
将变量值加上定义的数字,并将新值赋给该变量 |
$num1 += 10 |
|
|
–= |
将变量值减去定义的数字,并将新值赋给该变量 |
$num1 –= 10 |
|
|
*= |
将变量值乘以定义的数字,并将新值赋给该变量 |
$num1 *= 10 |
|
|
/= |
将变量值除以定义的数字,并将新值赋给该变量 |
$num1 /= 10 |
|
|
**= |
获取变量值的乘幂,并将新值赋给该变量 |
$num1 **= 3 $num2 = (3**= $num1) |
|
|
%= |
将变量值除以指定的数字,并将余数赋给该变量 |
$num1 %= 3 |
|
|
x= |
按指定次数重复执行字符串,并将新值赋给该变量 |
$jim x= 10 |
|
|
.= |
将字符串连接或追加到另一个字符串的结尾 |
$jim .= “my” $jim .= $foster |
|
大多数逻辑表达式或用逻辑运算符实现的表达式都用于流程控制结构中,以确定完成哪条执行路径。用这些运算符来比较表达式或变量,并且只返回true或false。表1-3给出了3种逻辑运算符的用法。
表1-3 Perl逻辑运算符
|
运 算 符 |
描 述 |
例 子 |
|
&& |
如果两个表达式都为true,则返回true |
(x= =1) && (y= =1) |
|
|| |
如果两个表达式中有一个为true,就返回true |
(x= =1) || (y= =1) |
|
! |
如果表达式为非true,则返回true |
!(cat= =dog) |
在多个表达式算法中,主要依赖运算符测试和量化表达式以及之间的差异。要注意,表达式所使用的所有关系运算符都返回逻辑值:true和false。表1-4列出了关系运算符的数值形式及其等价的字符串形式。
表1-4 Perl关系运算符
|
数 值 |
字 符 串 |
描 述 |
例 子 |
|
== |
eq |
两个值相等就返回true |
$num1 = =$num2 $foo eq “bar” |
|
!= |
ne |
两个值不相等就返回true |
$num1 != $num2 $foo ne “bar” |
|
> |
gt |
如果前面的值大于第二个值,则返回true |
$num1 > $num2 $foo gt “bar” |
|
< |
lt |
如果前面的值小于第二个值,则返回true |
$num1 < $num2 $foo lt “bar” |
|
数 值 |
字 符 串 |
描 述 |
例 子 |
|
>= |
ge |
如果前面的值大于或等于第二个值,则返回true |
$num1 >= $num2 $foo ge “bar” |
|
<= |
le |
如果前面的值小于或等于第二个值,则返回true |
$num1 <= $num2 $foo le “bar” |
字符串运算符用于控制、修改和检索字符串数据。除了字符串运算符以外,也常用多个特定字符串的正则表达来进行字符串的检索、模式匹配和替代操作。表1-5给出了Perl的字符串运算符。
表1-5 Perl字符串运算符
|
运 算 符 |
描 述 |
例 子 |
|
. |
返回第1个字符串后连接或追加了第二个字符串的新字符串 |
$foo.$bar |
|
x |
返回按指定次数串化过的字符串的值 |
$foo x $bar |
|
index() |
返回字符串在另一个字符串中的偏移量 |
$in = index($foo, $bar); |
|
substr() |
根据给定的索引值返回另一个字符串的子串 |
substr($foo, $in, $len) |
1.5.3 Perl脚本实例
例1-28包含了35行代码,用于在解析待测试的子网范围的时候生成IP地址列表。因为Perl脚本不能在GUI中执行,也没有自己的界面,所以变量的命令行解析就变得特别重要。range类型的数据是最难解析的数据之一,这主要是因为它通常包含多个令牌或表示多个变量。这些变量的抽取过程是很复杂、很麻烦的,如果没有使用合适的技术,甚至可能会抽取错误的结果。
例1-28 从命令行解析子网IP地址
1 #!/usr/bin/perl
2 if(@ARGV<2){print "Usage: $0 <network> <port>\nExample: $0 10.*.*.* 80
or 10.4.*.* 80 or 10.4.3.* 80\n";exit;}
3 else{
4 use IO::Socket;
5 $sIP="@ARGV[0]";
6 $port="@ARGV[1]";
7 ($ip1,$ip2,$ip3,$ip4)=split(/\./,$sIP);
8 if($ip2 == '*')
9 {$ip2=1; $ip3=1; $ip4=1; $x='a'; print "Scanning a Class A\n";}
10 elsif($ip3 == '*')
11 {$ip3=1; $ip4=1; $x='b'; print "Scanning a Class B\n";}
12 elsif($ip4 == '*')
13 {$ip4=1; $x='c'; print "Scanning a Class C\n";}
14
15 while($ip2<255 && $x == 'a')
16 {
17 while($ip3<255 && ($x == 'a' || $x == 'b'))
18 {
19 while($ip4<255)
20 {
21 $ipaddr="$ip1.$ip2.$ip3.$ip4";
22 print "$ipaddr\n";
23 #IP_connect($ipaddr);
24 $ip4++;
25 }
26 $ip4=1;
27 $ip3++;
28 if($x eq 'c') {$ip3=255; $ip2=255;}
29 }
30 $ip4=1;
31 $ip3=1;
32 $ip2++;
33 if($x eq 'c' || $x eq 'b') {$ip3=255; $ip2=255;}
34 }
35 }
分析
第1行通常用于指出在UNIX和Linux Perl配置中主Perl文件的位置,这在几乎所有的Win32系统和配置中是没有必要的。第2行用来检验是否通过命令行将两个参数传递给了脚本,如果少于两个参数,就会在STDOUT上打印出用法语句。
第5行和第6行用于设置传递给命令行的两个参数变量。注意,在解析这两个变量的值之前,没有对其进行检验。由于在此的主要目标是学习如何增加IP地址,因此省略了该检验过程。
第6行通过split函数将传入的IP地址分成4个整数。
第8到13行就是目的所在。可以通过检测传输了多少个星号打印出网络的大小并传给脚本。
程序的最后部分用一个循环语句增加IP地址,直到完成了目标范围。变量$ip4用于保存IP地址的最后8位的值,如10.9.5.123中最后8个位的值为123。因为$ip4位于嵌套循环内,所以它将一直增加直到值为255才停止,如第19行所示,然后返回到第17行的外层循环中。17行还指出,如果目标网络是类A和类B,那么外层循环将继续执行并增加$ip3的8位的值。
第23行的功能是,当传入的IP地址是当前IP地址的时候,就会调用一个虚构的函数。这是为了演示使用IP地址的简单性。最外层的循环只有当网络是类A的地址的时候才获得执行。注意,最外层的循环只能增加第2个8位的值,而不是第1个。






