【问题标题】:How does this perl code print that output?这个 perl 代码如何打印输出?
【发布时间】:2013-03-06 12:33:39
【问题描述】:

谁能解释这段代码如何打印输出?

代码

#!/usr/bin/perl
use warnings;
use strict;
       my          
      ($j,$        
   a,$p,$h);$      
   j=sub{print(    
  chr($p+=$a->[$   
  h++]));$j};;;$a  
 =[0,        split 
 "[:         \n]+", 
q/43         -002:1
-084         065:13
0001         000005
-0012        -00003
000013   -82 00048 
21:13:-6.00:-76:72 
 -007.:02:00008.00 
  :::-6.00:::013  
  -70:3::-70:.64   
    /];$p=0x4a     
      ;;$h=0;      
$j->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

输出:

Just another perl hacker

【问题讨论】:

标签: perl obfuscation


【解决方案1】:

这实际上是一个相当简单的 JAPH。它最大的两个特点是分割构成“图像”大部分的数据字符串,以及半递归打印。

这是我清理格式时得到的代码

my ($j,$a,$p,$h);
$j = sub {
    print( chr( $p += $a->[$h++] ) );
    $j
};
;;

$a = [0, split "[: \n]+",   # the split regex
q/43         -002:1         # input string start: q/ ...
-084         065:13
0001         000005
-0012        -00003
000013   -82 00048 
21:13:-6.00:-76:72 
 -007.:02:00008.00 
  :::-6.00:::013  
  -70:3::-70:.64   
    /];                     # input string end:   .../   
# print Dumper $a;  # <--- this is my line
$p = 0x4a;;
$h=0;      

$j->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

第一部分是4个主要变量的声明和赋值:

$j

$j 是一个代码引用,它打印一个字符,然后返回对自身的引用。打印包括几个步骤:

  1. $a-&gt;[$h++] 遍历 @$a 数组,返回一个元素
  2. $p += ... 该元素被添加到 $p
  3. print chr( $p ... ) 然后 $p 返回到 chr() 并打印结果字符。

sub 块的最后一个是$j,表示每次迭代,sub 的返回值都会是对自身的引用。这是允许半递归功能的部分。

$a

$a 是对包含 0 和输入字符串拆分结果的数组的引用。一行代码中还发生了一些事情:

  • q/43 .../ 这是带有分隔符斜杠 / 的常规 q() 单引号字符串。它在右括号 ] 之前结束,表示数组引用。
  • split "[: \n]+", 这是对字符类的拆分,重复 1 次或多次。它里面有额外的空间(我在这里删除了)来填充 JAPH 本身的空间。基本上这会删除所有冒号、空格和换行符并返回结果列表。
  • $a = [0, ... ] 拆分列表被添加到0 列表中(只有一个元素),所有内容都用括号括起来表示数组引用,然后将其存储在$a 中。

我假设列表以0 开头,因为代码有一次性错误,$p 的原始值是输出中的第一个字母。

这是$aData::Dumper 输出:

$VAR1 = [
          0,
          '43',
          '-002',
          '1',
          '-084',
          '065',
          '13',
          '0001',
          '000005',
          '-0012',
          '-00003',
          '000013',
          '-82',
          '00048',
          '21',
          '13',
          '-6.00',
          '-76',
          '72',
          '-007.',
          '02',
          '00008.00',
          '-6.00',
          '013',
          '-70',
          '3',
          '-70',
          '.64'
        ];

$p 和 $h

$p$h 只是数字,它们在其中的作用分别是chr() 函数的源编号和@$a 数组的迭代器。

最后一行是运行$j 子例程并打印JAPH 的部分。链式-&gt;() 语法意味着每次前一次迭代的返回值用于下一次执行。并且$j 返回一个对自身的引用,这使得它成为一个半递归的事情,只有值改变。

我猜它类似于这样做:

$x = $j->();
$y = $x->();
$z = $y->();
...

让我们来看看前几个步骤

  • $j-&gt;()执行
  • $a-&gt;[$h++] $h 递增到 1,返回 0 到 $a 子带,它返回 $a 的第一个元素,即 0
  • $p += 0 这个0 被添加到$p,这会将$p (74) 的值返回给chr
  • print chr(74) 现在将 J 打印到标准输出
  • $j 被返回。
  • -&gt;()对返回值又执行一次,所以$j再次运行。
  • $a-&gt;[$h++] $h 递增到 2,返回 1 到 $a,后者返回第二个元素 43
  • $p += 43 74 + 43 = 117 返回到chr
  • print chr(117) 这个打印 u

$a 的元素有正有负,将数字移动到需要的位置。最后,所有的字母都打印出来了:Just another Perl hacker,

以下是前 10 次处决的概要:

p => 74  += 0         =  74 output => chr( 74) J
p => 74  += 43        = 117 output => chr(117) u
p => 117 += -002      = 115 output => chr(115) s
p => 115 += 1         = 116 output => chr(116) t
p => 116 += -084      =  32 output => chr( 32)
p => 32  += 065       =  97 output => chr( 97) a
p => 97  += 13        = 110 output => chr(110) n
p => 110 += 0001      = 111 output => chr(111) o
p => 111 += 000005    = 116 output => chr(116) t
p => 116 += -0012     = 104 output => chr(104) h

【讨论】:

  • @deadlock 这是一个有点有趣的解码算法。 :)
  • @deadlock 别误会,这很有趣,但把它和这个比较一下:stackoverflow.com/q/9591658/725418 看着它真的很伤你的眼睛。 :)
【解决方案2】:

perl -MO=Deparse 运行它,看看 Perl 看到了什么:

use warnings;
use strict 'refs';
my($j, $a, $p, $h);
$j = sub {
    print chr($p += $$a[$h++]);
    $j;
}
;
$a = [0, split(/[:         \n]+/, "43         -002:1\n-084         065:13\n0001         000005\n-0012        -00003\n000013   -82 00048 \n21:13:-6.00:-76:72 \n -007.:02:00008.00 \n  :::-6.00:::013  \n  -70:3::-70:.64   \n    ", 0)];
$p = 74;
$h = 0;
&$j()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

基本上,$j 是一个子引用,它返回另一个子引用,依此类推。在返回之前,它会打印一个字符并将“指针”移动到要打印的字符数组中。

【讨论】:

  • 那么,$a 中的 ASCII 值是否与输出对应?我无法理解。
  • 来自chr($p += $$a[$h++])的猜测,它们比较对应差异。
【解决方案3】:

小评论。

数字块的最后一行应该是这个

-68:3::-70:.64

而不是

-70:3::-70:.64   

需要-68来打印句点,-70是打印逗号。

(该行的其余部分,":3::-70:.64" 只是噪音。这些数字永远不会传递给 $j 中的匿名函数。)

【讨论】:

    猜你喜欢
    • 2020-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 2017-11-12
    • 2012-11-27
    • 1970-01-01
    相关资源
    最近更新 更多