解包一个 PAR 打包的 perl 程序源码
最近在流程debug时,和同事发现一个perl脚本有问题;然后发现这个perl脚本是别人打包好的,所以看不到源码。我就想把这个perl脚本给解包(不敢称为反编译,因为实际上也没做反编译)了。因为打包perl脚本这事,我以前也做过,所以我觉得应该可行。
如何用PAR来打包perl程序
首先,一般打包perl程序,现在应该是使用PAR::Packer打包perl程序,具体参数参考http://search.cpan.org/~rschupp/PAR-1.015/lib/PAR/Tutorial.pod。打包的命令如下:
pp -g -B -o xxx xxx.pl # -g 生成二进制程序, -B 将各种依赖项打包进去, -o 生成的文件名
解包PAR打包的perl程序
首先运行一次程序,然后在 /tmp/
目录下,查看是否有 par-
开头的文件夹,PAR打包的程序有个特点,就是会将程序解压到 /tmp
目录下,然后再运行。
如果有这样子的目录,应该就是PAR打包的了。然后我们使用 unzip -d unarchive xxx
,xxx是你的程序的名称,这样子我们就能得到解压的目录。(xxx.zip,语雀对上传文件的后缀有限制,大家自行解压,然后执行exe文件测试)
发现是Bleach加密过的源码
一般解压后我们就能得到脚本的原始.pl
文件,但是这次,我打开 xxx.pl
文件,却只看到一行很奇怪的代码,而文件的大小却很大。这里上传一个xxx.pl给大家玩一下😄️。
$_=<<'';y;\r\n;;d;y; \t;01;;$_=pack'b*',$_;$_=eval;$@&&die$@;$_
是的,整个文件,我wc了一下,足足有2000多行,但是打开来看,却只看到一行代码,这时,我就猜想,这里应该是使用了什么方法来加密的代码,将代码替换成不可见的字符。
于是我使用 cat -vet xxx.pl| less -SN
来查看该文件,发现这个文件只有这种类似与tab的字符,
$_=<<'';y;\r\n;;d;y; \t;01;;$_=pack'b*',$_;$_=eval;$@&&die$@;$_$
^I^I ^I ^I$
^I ^I^I$
^I^I ^I ^I ^I$
幸运的是,当我将 $_=<<'';y;\r\n;;d;y; \t;01;;$_=pack'b*',$_;$_=eval;$@&&die$@;$_$
这串字符串拿去google之后,很快发现了A Beginner’s Guide to Compiling Perl Scripts这篇文章,原来这种源码是经过Bleach这种方法加密过的。
当然,这里我还在stackoverflow查到这种方法是如何生效的。
$_ = << '';
reads the rest of the file into the accumulator.y;\r\n;;d;
strips carriage returns and line feeds.$_ = pack 'b*', $_;
converts characters to bits in$_
, LSB first.$_ = eval;
executes$_
as Perl code.$@ && die $@; $_
handles exceptions and the return code gracefully.
解密Bleach加密的源码
明白是怎么加密之后,我就开始找如何解密的方法,一开始我找到一个unbleach.pl的代码,但是运行了之后,可能是这个代码太老了,并不能解码。
然后我看到How does this obfuscated Perl script work?和bleach question,一下子就醒悟过来,上面的原理解释里不是说了吗,用 eval
来执行 $_
里的perl代码,那将 eval
修改为 print
,就变成了将代码都打印出来😄️。
原来那么简单!
最后我修改文件头一行为
$_=<<'';y;\r\n;;d;y; \t;01;;$_=pack'b*',$_;$_=print;$@&&die$@;$_
然后执行 perl xxx.pl | less -SN
,终于看到源码了。
讨论
其实,我在几年前,也做过加密打包perl代码的工作,然后当时就发现,这个打包然并卵。首先会解压到 /tmp
目录下,其次 unzip
一下也能解包,当时我也有看到 PAR::Filter::Bleach
和 PAR::Filter::Bytecode
等加密方法,以为加上这个参数应该就可以,但现在发现这种方法还是不安全的。
另外,我看了A Beginner’s Guide to Compiling Perl Scripts里面加上 PAR::Filter::Crypto
的方法,我看到程序要解密加密后的代码,代码必须将密钥也写在代码里,所以应该也是然并卵的。
dev@virtualbox:~/Desktop/decompile$ more script/bestProgram.pl
use Filter::Crypto::Decrypt;
e6ad69e3dd1e9901ccf6ba701ff66dfb09ba6b2f6c3b872e33e1929e56298f9861777c6a21255012
c658fa873214cd41071f6915ef594ee5447ae02afc1e2d726333fb855148920518e827fbb0990053
73eddabe4e608e15fcc54008c659f218ac32d56ac8d05a78cfd446ae05cf6c19f7e3c1b30fab747f
c0b0017243f764b3d3db0ef081bbf245478bd5a6af4c361b22e488edf203d91d4018d930fe4d2c1b
8d4d103810eb0603
dev@virtualbox:~/Desktop/decompile$ ../bestProgram_Encrypted.exe
$VAR1 = {
'second' => 2,
'first' => 1,
'third' => 3
};
最好的方法,还是将perl代码转换为c代码,甚至是c代码的模块的形式,增加反编译的难度。