otm*_*mar 11 perl reverse-engineering
朋友的服务器(是的,真的.不是我的.)被打破了,我们发现了一个运行一些机器人代码的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,
},
}
);
Run Code Online (Sandbox Code Playgroud)
运行时显示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'};
Run Code Online (Sandbox Code Playgroud)
然而,这意味着任何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 );
Run Code Online (Sandbox Code Playgroud)
有关更温和的介绍OP*和相关的想法,请参阅更新的
PerlGuts Illustrated和Optree内容.