A friend’s server (yes, really. Not mine.) was broken into and we discovered a perl binary running some bot code. We could not find the script itself (probably eval’ed as received over the network), but we managed to create a core dump of the perl process.
Running strings on the core gave us some hints (hostnames, usernames / passwords), but not the source code of the script.
We’d like to know what the script was capable of doing, so we’d like to reverse-engineer the perl code that was running inside that perl interpreter.
Searching around, the closest thing to a perl de-compiler I found is the B::Deparse module which seems to be perfectly suitable for converting the bytecode of the parse-trees back into readable code.
Now, how do I get B::Deparse to operate on a core dump? Or, alternatively, how could I restart the program from the core, load B::Deparse and execute it?
Any ideas are welcome.
ysth asked me on IRC to comment on your question. I’ve done a whole
pile of stuff “disassembling” compiled perl and stuff (just see my
CPAN page [http://search.cpan.org/~jjore%5D).
Perl compiles your source to a tree of
OP*structs whichoccasionally have C pointers to
SV*which are perl values. Your coredump now has a bunch of those
OP*andSV*stashed.The best possible world would be to have a perl module like
B::Deparse do the information-understanding work for you. It
works by using a light interface to perl memory in the
B::OPandB::SVclasses (documented in B, perlguts, andperlhack). This is unrealistic for you because a
B::*object isjust a pointer into memory with accessors to decode the struct for our
use. Consider:
which when run showed that the
B::PVobject (it is ISAB::SV) istruely merely an interface to the memory representation of the
compiled string
this is a string.This however implies that any
B::*using code must actually operateon live memory. Tye McQueen thought he remembered a C debugger which
could fully revive a working process given a core dump. My
gdbcan’t.
gdbcan allow you to dump the contents of yourOP*andSV*structs. You would most likely just read the dumped structs tointerpret your program’s structure. You could, if you wished, use
gdbto dump the structs, then synthetically createB::*objectswhich behaved in interface as if they were ordinary and use
B::Deparseon that. At root, our deparser and other debug dumpingtools are mostly object oriented so you could just “fool” them by
creating a pile of fake
B::*classes and objects.You may find reading the B::Deparse class’s
coderef2textmethodinstructive. It accepts a function reference, casts it to a
B::CVobject, and uses that for input to the
deparse_submethod:For gentler introductions to
OP*and related ideas, see the updatedPerlGuts Illustrated and Optree guts.