朋友的服务器(是的,真的.不是我的.)被打破了,我们发现了一个运行一些机器人代码的perl二进制文件.我们找不到脚本本身(可能是通过网络收到的eval'),但我们设法创建了perl进程的核心转储.
在核心上运行字符串给了我们一些提示(主机名,用户名/密码),但不是脚本的源代码.
我们想知道脚本能够做什么,所以我们想对perl解释器中运行的perl代码进行反向工程.
搜索周围,我发现最接近perl解编译器的是B :: Deparse模块,它似乎非常适合将解析树的字节码转换回可读代码.
现在,我如何让B :: Deparse在核心转储上运行?或者,或者,如何从核心重新启动程序,加载B :: Deparse并执行它?
欢迎任何想法.
ysth让我在IRC上评论你的问题.我已经完成了一堆"反汇编"编译的perl和东西(只看我的CPAN页面[ http://search.cpan.org/~jjore]).
Perl将你的源代码编译成一个结构树OP*
,偶尔会有一个C指针,SV*
它们是perl值.你的核心转储现在有很多这些OP*
并存储起来SV*
.
最好的世界将是像B :: Deparse这样的perl模块
为您做信息理解工作.它的工作原理是使用一个轻型接口来记录B::OP
和
B::SV
类中的perl内存(记录在B,perlguts和
perlhack中).这对你来说是不现实的,因为B::*
对象只是一个指向内存的指针,带有访问器来解码结构供我们使用.考虑:
require Data::Dumper; require Scalar::Util; require B; my $value = 'this is a string'; my $sv = B::svref_2object( \ $value ); my $address = Scalar::Util::refaddr( \ $value ); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Purity = 1; print Data::Dumper::Dumper( { address => $address, value => \ $value, sv => $sv, sv_attr => { CUR => $sv->CUR, LEN => $sv->LEN, PV => $sv->PV, PVBM => $sv->PVBM, PVX => $sv->PVX, as_string => $sv->as_string, FLAGS => $sv->FLAGS, MAGICAL => $sv->MAGICAL, POK => $sv->POK, REFCNT => $sv->REFCNT, ROK => $sv->ROK, SvTYPE => $sv->SvTYPE, object_2svref => $sv->object_2svref, }, } );
运行时显示B::PV
对象(它是ISA B::SV
)实际上只是编译字符串的内存表示的接口this is a string
.
$VAR1 = { 'address' => 438506984, 'sv' => bless( do{\(my $o = 438506984)}, 'B::PV' ), 'sv_attr' => { 'CUR' => 16, 'FLAGS' => 279557, 'LEN' => 24, 'MAGICAL' => 0, 'POK' => 1024, 'PV' => 'this is a string', 'PVBM' => 'this is a string', 'PVX' => 'this is a string', 'REFCNT' => 2, 'ROK' => 0, 'SvTYPE' => 5, 'as_string' => 'this is a string', 'object_2svref' => \'this is a string' }, 'value' => do{my $o} }; $VAR1->{'value'} = $VAR1->{'sv_attr'}{'object_2svref'};
然而,这意味着任何B::*
使用代码必须实际上在实时内存上运行.Tye McQueen认为他记得一个C调试器,它可以完全恢复给定核心转储的工作过程.我gdb
不能.gdb
可以允许您转储OP*
和
SV*
结构的内容.您很可能只是阅读转储的结构来解释程序的结构.如果你愿意的话,你可以使用
gdb
转储结构,然后合成地创建B::*
在接口中表现的对象,就好像它们是普通的并B::Deparse
在其上使用一样
.从根本上说,我们的deparser和其他调试转储工具大多是面向对象的,所以你可以通过创建一堆伪B::*
类和对象来"欺骗"它们.
您可能会发现阅读B :: Deparse类的coderef2text
方法具有指导意义.它接受函数引用,将其强制转换为B::CV
对象,并将其用于deparse_sub
方法的输入:
require B; require B::Deparse; sub your_function { ... } my $cv = B::svref_2object( \ &your_function ); my $deparser = B::Deparse->new; print $deparser->deparse_sub( $cv );
有关更温和的介绍OP*
和相关的想法,请参阅更新的
PerlGuts Illustrated和Optree内容.