朋友的服务器(是的,真的。不是我的。)被打破了,我们发现了一个运行一些机器人代码的perl二进制文件。我们找不到脚本本身(可能是通过网络收到的eval'),但我们设法创建了perl进程的核心转储。
在核心上运行字符串给了我们一些提示(主机名,用户名/密码),但不是脚本的源代码。
我们想知道脚本能够做什么,所以我们想对perl解释器中运行的perl代码进行反向工程。
搜索周围,我发现的最接近perl解编译器的是B :: Deparse模块,它似乎非常适合将解析树的字节码转换回可读代码。
现在,我如何让B :: Deparse在核心转储上运行?或者,或者,如何从核心重新启动程序,加载B :: Deparse并执行它?
欢迎任何想法。
答案 0 :(得分:7)
Perl将您的源代码编译为OP*
结构树
偶尔会有SV*
的C指针,它们是perl值。你的核心
dump现在有一堆OP*
和SV*
被隐藏。
最好的世界就是拥有一个perl模块
B::Deparse为您完成信息理解工作。它
通过在B::OP
和B::SV
中使用光接口来实现内存
B::*
个类(记录在B,perlguts和
perlhack)。这对你来说是不现实的,因为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
运行时显示B::SV
对象(它是ISA 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::*
但这意味着任何gdb
使用代码必须实际运行
在现场记忆中。 Tye McQueen认为他记得一个C调试器
在核心转储的情况下,可以完全恢复工作流程。我的gdb
不能。 OP*
可以允许您转储SV*
和gdb
的内容
B::*
结构。你很可能只是阅读转储的结构
解释你的程序的结构。如果你愿意,你可以使用
B::Deparse
转储结构,然后合成创建B::*
个对象
在界面中表现得好像是普通的并且使用它们
coderef2text
就此而言。在root,我们的deparser和其他调试转储
工具大多是面向对象的,所以你可以“愚弄”它们
创建一堆伪造的B::CV
类和对象。
您可能会发现阅读B :: Deparse类的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*
方法:
{{1}}
有关{{1}}及相关提示的更温和的介绍,请参阅更新 PerlGuts Illustrated和Optree guts。
答案 1 :(得分:2)
我怀疑有一个开箱即用的工具,所以......
找到您运行的perl版本的源代码。这应该有助于您了解perl解释器的内存布局。它还可以帮助你弄清楚是否有办法在这里采用一个快捷方式(例如,如果字节码前面有一个易于查找的内存或其他东西)。
在调试器中加载二进制+核心转储,可能是gdb
使用perl源代码中的信息来指导您说服调试器吐出您感兴趣的字节码。
一旦你有字节码,B :: Deparse应该能够让你更具可读性。
答案 2 :(得分:0)
好吧,undump会将核心转储重新转换为二进制可执行文件(如果可以找到工作版本)。然后,您应该可以将其加载到perl
和-MO=Deparse
。