Grep任何空白字符,包括单一模式中的换行符

时间:2016-06-24 08:57:57

标签: regex grep

我正在尝试使用'perfect'命令在dir或包含eval代码的子目录中显示任何.php文件。

由于存在许多误报,我会在解决方案之后删除至少最明显的误报 - 所以我的目标是:

单词eval,后跟任何空格字符,包括换行符0到无限次,后跟开括号字符(;

以下是我的镜头:

find . -type f -exec grep -l "eval\s*(" {} \; | grep ".php"

效果不错某种方式\s*这里与换行符不匹配,所以

eval

("some nasty obfuscated code");

低于雷达。

我也尝试过:

find . -type f -exec grep -l "eval[[:space:]]*(" {} \; | grep ".php"

结果相同。

2 个答案:

答案 0 :(得分:1)

如果我理解你是对的,那么我相信这一行是您正在寻找的:

find . -name '*.php' -exec grep -Ezl 'eval\s*\(' {} +

-z是您一直缺少的,请参阅下面的说明。 当然你可以给find命令提供其他任何根而不是.,只需根据你在哪里寻找参数和条件以及你在寻找什么。

就是这样。从这里开始,解释:

find命令

在大多数情况下,首先搜索扩展名为.php的文件可能会更快,然后只在这些文件中搜索正则表达式。 -name '*.php'部分通过仅搜索文件名以' .php'结尾的文件为我们提供此行为。

-exec允许我们使用find命令的输出(文件名)执行命令。我们正在使用它来为所有php文件执行grep。

该行末尾的语法{} +创建一个长文件名列表作为grep命令的参数,而不是为每个文件单独执行grep。

grep命令

-E:将PATTERN解释为扩展正则表达式(从grep手册页复制)

-z:将输入视为一组行,每行以零字节而不是换行符(grep手册页)结束。这意味着对于普通文本文件,整个文件将被视为一个长行。此行为允许您使用多行正则表达式。

-l:告诉grep只显示与搜索匹配的所有文件的文件名,而不是显示匹配的行。

正则表达式

'eval'只匹配eval这个词。 '\s'匹配任何空白字符,而'*'表示它可能出现零次或多次。这个'\('与实际的括号相匹配,在这种情况下需要转义(以及\的用途)。

玩得开心!

答案 1 :(得分:0)

简单版本:

为了简单起见,为了满足您的需求,但使用awk代替grep(如果可能的话),那么对于/tmp/中的php文件,您可以简单地; < / p>

awk -v RS="^$" '/eval[[:space:]]*\(/ { print FILENAME }' /tmp/*.php

这将打印匹配的文件。

如果您需要使用find的输出:

find /tmp/ -iname "*.php" -print | while read file ; do awk -v RS="^$" '/eval[[:space:]]*\(/ { print FILENAME }' "$file" ; done

以上内容很简单,即使使用busybox和awk的基本版本也可以使用。

替代(有匹配)

这部分答案对某些人来说可能看起来很荒谬,但是在搜索空白方面有足够的经验,并且在shell中进行序列化,“陷阱”的数量变得明显,并且对工作解决方案的需求导致对构建的偏好 - 在一个衬里后退。

这也可能有助于其他人遇到类似的需求,但需要易于阅读的行预览,可能是解析或简单:

注意1:此解决方案适用于sh / ash / busybox以及bash(仍然需要外部二进制文件xxd

注2:对于 BSD grep,请将-P替换为-E。在支持-E的GNU grep上使用-P,似乎不会产生相同的前瞻匹配

示例测试文件

获取此测试文件(带有特殊字符的注释),以及位于/ tmp /中的其他2个测试文件:

eval file

find /tmp/ -iname "*.php" -print \
| while read file ; do hexdump -ve '1/1 " %02X"' "$file" \
| sed -E "s/($)/ 0A/g" \
| grep -P -o "65 76 61 6C( 09| 0A| 0B| 0C| 0D| 20)*? 28 22.+?0A" \
| sed -E -e 's/ //g' \
| sed -E -e 's/(0A)+([^$])/20\2/g' \
| sed -E -e 's/(09|0B|0C|0D|20)+/20/g' \
| xxd -r -p \
| grep -i "eval" && printf "$file matches\n\n" ; done

将返回匹配项,从eval,返回到("匹配的行的末尾,将换行符和空格替换为单个空格为了便于阅读:

eval ("some nasty obfuscated code (LF / LINE FEED)");
eval ("some nasty obfuscated code (HT / TAB)");
eval ("some nasty obfuscated code (SP / SPACE)");
eval ("some nasty obfuscated code (FF / FORM FEED)");
eval ("some nasty obfuscated code (CR / CARRIAGE RETURN)");
eval ("some nasty obfuscated code (VT / VERTICAL TAB)");
eval ("some nasty obfuscated code (LF > HT > FF > CR > LF > LF > HT > VT > LF > HT > SP)");
eval ("some nasty obfuscated code (VT / VERTICAL TAB)");
/tmp/eval.php matches

eval ("some nasty obfuscated code (LF / LINE FEED)");
/tmp/eval_no_trailing_line_feed.php matches

eval("\$str = \"$str\";");
/tmp/eval_w3_example.php matches

对于使用此方法的文件匹配(例如,可能允许使用“-v”选项),只需将最后一行的grep -i更改为grep -iq

说明:

find /tmp/ -iname "*.php" -print \:在/ tmp /

中查找.php文件

| while read file ; do hexdump -ve '1/1 " %02X"' "$file" \:hexdump每个结果文件,并以单个空格分隔字节输出(以避免从一个字节的第二个字符到另一个字节的第一个字符匹配)

| sed -E "s/($)/ 0A/g" \:将一个0A(换行符)放在匹配的文件的最后 - 这意味着它将匹配没有尾随换行符的文件(有时可能导致文本处理的一些问题)

| grep -P -o "65 76 61 6C( 09| 0A| 0B| 0C| 0D| 20)*? 28 22.+?0A" \:仅返回匹配(请注意grep为每个匹配添加换行符)

  • 6576616C:eval
  • 09:水平TAB
  • 0A:换行
  • 0B:vertical TAB
  • 0C:换页
  • 0D:回车
  • 20:飞机空间
  • 2822 :(“

| sed -E -e 's/ //g' \:删除字节之间的所有空格(最后可能不需要)

| sed -E -e 's/(0A)+([^$])/20\2/g' \:查找0A(换行符)的重复出现次数,只要它们不是该行末尾的换行符,并将其替换为单个空格({ {1}})

20:查找上方的任何空白字符,并将其替换为空格,以便于阅读

| sed -E -e 's/(09|0B|0C|0D|20)+/20/g' \:从十六进制恢复

| xxd -r -p \:打印匹配项和文件名(| grep -i "eval" && printf "$file matches\n\n" ; done表示&&仅打印文件匹配项,如果printf的输出为{{{ 1}}(成功),因此它不会简单地打印循环中的每个文件。(如前所述,将grep添加到此grep中仍将为0的目的进行评估,但不会输出匹配的行。