如何正确反混淆Perl脚本?

55

我正在尝试反混淆以下Perl代码(源码):

#!/usr/bin/perl
(my$d=q[AA                GTCAGTTCCT
  CGCTATGTA                 ACACACACCA
    TTTGTGAGT                ATGTAACATA
      CTCGCTGGC              TATGTCAGAC
        AGATTGATC          GATCGATAGA
          ATGATAGATC     GAACGAGTGA
            TAGATAGAGT GATAGATAGA
              GAGAGA GATAGAACGA
                TC GATAGAGAGA
                 TAGATAGACA G
               ATCGAGAGAC AGATA
             GAACGACAGA TAGATAGAT
           TGAGTGATAG    ACTGAGAGAT
         AGATAGATTG        ATAGATAGAT
       AGATAGATAG           ACTGATAGAT
     AGAGTGATAG             ATAGAATGAG
   AGATAGACAG               ACAGACAGAT
  AGATAGACAG               AGAGACAGAT
  TGATAGATAG             ATAGATAGAT
  TGATAGATAG           AATGATAGAT
   AGATTGAGTG        ACAGATCGAT
     AGAACCTTTCT   CAGTAACAGT
       CTTTCTCGC TGGCTTGCTT
         TCTAA CAACCTTACT
           G ACTGCCTTTC
           TGAGATAGAT CGA
         TAGATAGATA GACAGAC
       AGATAGATAG  ATAGAATGAC
     AGACAGAGAG      ACAGAATGAT
   CGAGAGACAG          ATAGATAGAT
  AGAATGATAG             ACAGATAGAC
  AGATAGATAG               ACAGACAGAT
  AGACAGACTG                 ATAGATAGAT
   AGATAGATAG                 AATGACAGAT
     CGATTGAATG               ACAGATAGAT
       CGACAGATAG             ATAGACAGAT
         AGAGTGATAG          ATTGATCGAC
           TGATTGATAG      ACTGATTGAT
             AGACAGATAG  AGTGACAGAT
               CGACAGA TAGATAGATA
                 GATA GATAGATAG
                    ATAGACAGA G
                  AGATAGATAG ACA
                GTCGCAAGTTC GCTCACA
])=~s/\s+//g;%a=map{chr $_=>$i++}65,84,67,
71;$p=join$;,keys%a;while($d=~/([$p]{4})/g
){next if$j++%96>=16;$c=0;for$d(0..3){$c+=
$a{substr($1,$d,1)}*(4**$d)}$perl.=chr $c}
             eval $perl;

运行时,它会打印出Just another genome hacker.

通过Deparseperltidyperl -MO=Deparse jagh.pl | perltidy)运行代码后,代码看起来像这样:

( my $d =
"AA...GCTCACA\n" # snipped double helix part
) =~ s/\s+//g;
(%a) = map( { chr $_, $i++; } 65, 84, 67, 71 );
$p = join( $;, keys %a );
while ( $d =~ /([$p]{4})/g ) {
    next if $j++ % 96 >= 16;
    $c = 0;
    foreach $d ( 0 .. 3 ) {
        $c += $a{ substr $1, $d, 1 } * 4**$d;
    }
    $perl .= chr $c;
}

这是我自己能够破译的内容。

( my $d =
"AA...GCTCACA\n" # snipped double helix part
) =~ s/\s+//g;

去除 $d(双螺旋)中的所有空格。

(%a) = map( { chr $_, $i++; } 65, 84, 67, 71 );

ATCG 作为键,分别赋值为 0123,生成哈希表。

我通常使用 Python 进行编程,因此在 Python 中,这可以转换为一个字典 {'A': 0, 'B': 1, 'C': 2, 'D': 3}

$p = join( $;, keys %a );

使用$;将哈希键连接起来,用于模拟多维数组的下标分隔符。文档中说默认值为"\034",与awk中的SUBSEP相同,但当我执行以下操作时:

my @ascii = unpack("C*", $p);
print @ascii[1];

我得到了值为28?而且我不清楚这如何模拟多维数组。现在的$p是否类似于Python中的[['A'], ['T'], ['C'], ['G']]

    while ( $d =~ /([$p]{4})/g ) {

只要$d([$p]{4})匹配,就执行while块中的代码。但由于我不完全理解$p的结构,所以我也很难理解这里发生了什么。
next if $j++ % 96 >= 16;

如果 $j 取模 96 的结果大于等于16,则继续执行。在 while 循环中,$j 每次递增。
$c = 0;
foreach $d ( 0 .. 3 ) {
    $c += $a{ substr $1, $d, 1 } * 4**$d;
}

对于范围从03$d,提取一些子字符串,但是此时我完全迷失了。最后几行将所有内容连接起来并评估结果。


17
在处理使用eval执行的混淆字符串代码时,你应该始终小心谨慎。我在stackoverflow上看到过一个狡猾的问题,其中最后使用eval执行的字符串实际上是 "rm -rf /"。请注意保护自己免受此类攻击。 - TLP
2
让我想起了Acme::EyeDrops - David Mertens
1个回答

51

注意:不要盲目运行混淆的perl代码,特别是其中有eval、反引号、systemopen等调用的情况,并且这可能并不太明显。在使用Deparse进行反混淆并仔细将eval替换为打印语句之前,必须理解其含义。也应考虑在沙箱/非特权用户/虚拟机中运行。

*s&&$_ⅇ例如对$_进行评估。


首先观察:034是八进制的。它等于28(十进制)或0x1c(十六进制),因此没有任何可疑之处。

$;只是纯粹的混淆,找不到使用它的原因。 $p将只是一个字符串A.T.C.G(用$;替换.,无论是什么)。因此,在正则表达式[$p]中,匹配{'A','T','C','G',$;}中的任何一个。由于$;从未出现在$d中,因此它在那里是无用的。 反过来,[$p]{4}匹配上述集合中的任何四个字母的序列,就好像使用了这个(忽略无用的$;):

while ( $d =~ /([ATCG]{4})/g ) { ... }

如果你需要自己编写这段代码,那么在去除空格后,你只需每次获取长度为四的 $d 的连续子字符串(假设 $d 中没有其他字符)。

现在,让我们进入有趣的部分:

foreach $d ( 0 .. 3 ) {
    $c += $a{ substr $1, $d, 1 } * 4**$d;
}
  • $1 holds the current four-letter codepoint. substr $1, $d, 1 returns each successive letter from that codepoint.
  • %a maps A to 00b (binary), T to 01b, C to 10b, and G to 11b.

    A   00
    T   01
    C   10
    G   11
    
  • multiplying by 4**$d will be equivalent to a bitwise left shift of 0, 2, 4 and 6.

这个有趣的结构允许你使用ATCG作为数字,在四进制系统中构建任何8位值!

也就是说,它执行以下转换:

         A A A A
AAAA -> 00000000

         T A A T
TAAT -> 01000001 -> capital A in ascii

         T A A C
CAAT -> 01000010 -> capital B in ascii

CAATTCCTGGCTGTATTTCTTTCTGCCT -> BioGeek

这部分内容:

next if $j++ % 96 >= 16;

只针对前16个“代码点”,跳过接下来的80个,然后再转换下一个16个,再跳过下一个80个,以此类推。这实质上只是跳过了椭圆形(垃圾DNA去除系统)的一部分。


以下是一个丑陋的文本到DNA转换器,您可以使用它来生成任何东西来替换螺旋体(不处理80个字符跳过的问题):

use strict;
use warnings;
my $in = shift;

my %conv = ( 0 => 'A', 1 => 'T', 2 => 'C', 3 => 'G');

for (my $i=0; $i<length($in); $i++) {
    my $chr = substr($in, $i, 1);
    my $chv = ord($chr);
    my $encoded ="";
    $encoded .= $conv{($chv >> 0) & 0x3};
    $encoded .= $conv{($chv >> 2) & 0x3};
    $encoded .= $conv{($chv >> 4) & 0x3};
    $encoded .= $conv{($chv >> 6) & 0x3};
    print $encoded;
}
print "\n";

$ perl q.pl 'print "BioGeek\n";'
AAGTCAGTTCCTCGCTATGTAACACACACAATTCCTGGCTGTATTTCTTTCTGCCTAGTTCGCTCACAGCGA

将螺旋替换为$d(并从解码器中删除跳过部分)。


5
非常好 :) 关于你的警告,有一件事情需要注意:eval 部分也可以被混淆。我在主评论中提到的代码使用了类似 ;;s;;$_;see 的东西,这是对 $_ 进行了双重求值(double eval)。 - TLP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接