2010-08-26 12 views
11

Il server di un amico (sì, proprio. Non il mio.) È stato interrotto e abbiamo scoperto un binario perl con codice bot in esecuzione. Non siamo riusciti a trovare lo script stesso (probabilmente valutato come ricevuto attraverso la rete), ma siamo riusciti a creare un core dump del processo perl.Reverse Engineering uno script Perl basato su un core dump

L'esecuzione di stringhe sul core ci ha fornito alcuni suggerimenti (nomi degli host, nomi utente/password), ma non il codice sorgente dello script.

Vorremmo sapere che cosa lo script è in grado di fare, quindi vorremmo eseguire il reverse-engineering del codice perl eseguito all'interno dell'interprete perl.

Ricerca in giro, la cosa più vicina a un de-compiler perl trovato è il modulo B :: Deparse che sembra essere perfettamente adatto per convertire il bytecode degli alberi di analisi in codice leggibile.

Ora, come si ottiene B :: Deparse per operare su un core dump? Oppure, in alternativa, come posso riavviare il programma dal core, caricare B :: Deparse ed eseguirlo?

Tutte le idee sono benvenute.

risposta

7

ysth mi ha chiesto su IRC di commentare la sua domanda. Ho fatto un intero mucchio di roba perl per "smontare" compilato perl e roba da (leggi la mia pagina CPAN [http://search.cpan.org/~jjore]).

Perl compila la sorgente su un albero delle strutture OP* che a occasionalmente ha puntatori C su SV* che sono valori perl. Il tuo core dump ora ha un mucchio di quelli OP* e SV* nascosti.

Il miglior mondo possibile sarebbe avere un modulo perl come B::Deparse fare il lavoro di comprensione delle informazioni per voi. Si funziona utilizzando un'interfaccia luce perl memoria nelle classi B::OP e B::SV (documentato in B, perlguts e perlhack).Questo non è realistico per te perché un oggetto B::* è solo un puntatore in memoria con gli accessori per decodificare la struttura per il nostro uso di . Considerare:

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, 
    }, 
    } 
); 

che quando viene eseguito ha mostrato che l'oggetto B::PV (è ISA B::SV) è veramente solo un'interfaccia alla rappresentazione memoria della stringa compilato 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'}; 

Ciò implica, tuttavia, che qualsiasi B::* utilizzando il codice deve essere effettivamente operare sulla memoria dal vivo. Tye McQueen ha pensato di ricordare un debugger C che potrebbe far rivivere completamente un processo di lavoro dato un core dump. Il mio gdb non può. gdb può consentire di scaricare il contenuto delle strutture OP* e SV*. Molto probabilmente leggerete semplicemente le strutture scaricate su interpretando la struttura del vostro programma. Se lo desideri, puoi utilizzare gdb per eseguire il dump delle strutture, quindi creare sinteticamente gli oggetti B::* che si comportano nell'interfaccia come se fossero ordinari e utilizzare B::Deparse su quello. Alla radice, il nostro depharger e altri strumenti di debugging sono per lo più orientati agli oggetti in modo da poterli "ingannare" da creando una pila di classi e oggetti falsi B::*.

È possibile trovare istruttivo il metodo coderef2text della classe B :: Deparse . Si accetta un riferimento alla funzione, l'inserisce in un oggetto B::CV , e lo utilizza per l'ingresso al metodo 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); 

Per introduzioni più delicato di OP* e idee correlate, vedere la versione aggiornata PerlGuts Illustrated e Optree guts.

+0

Josh, grazie per la risposta dettagliata. Questo praticamente un jive con quello che mi aspettavo. Sembra un progetto per lunghe notti invernali. – otmar

0

Bene, undump trasformerà il core dump in un eseguibile binario (se è possibile trovare una versione funzionante). Dovresti quindi poterlo caricare in perl e -MO=Deparse.

+2

Err, penso che ci sia un difetto lì; come si carica un eseguibile binario in perl? – ysth

+0

Ho avuto l'impressione che abbia appena fatto 'perl a.out' uno di questi giorni dopo un tutorial di Par :: Packer, ma l'ho provato ora e non ha funzionato. –

2

Dubito che ci sia uno strumento là fuori che fa questo, fuori dalla scatola, quindi ...

  1. trovare il codice sorgente per la versione di Perl si sta eseguendo. Questo dovrebbe aiutare a capire il layout di memoria dell'interprete perl. Ti aiuterà anche a capire se c'è un modo per prendere una scorciatoia qui (ad esempio se il bytecode è preceduto da un'intestazione facile da trovare in memoria o qualcosa del genere).

  2. di carico fino al binario core dump + in un debugger, probabilmente gdb

  3. Utilizzare le informazioni nel codice sorgente Perl per guidare l'utente nel convincere il debugger per sputare il bytecode che ti interessa.

Una volta ottenuto il bytecode, B :: Deparse dovrebbe essere in grado di portarvi a qualcosa di più leggibile.

+0

Sì, sembra ragionevole. Potrebbe esserci un problema, tuttavia: la documentazione di perl fa riferimento al codice byte come albero di parsing. Ciò suggerisce che il byte-code non è un array di codici operativi indipendente dal suo indirizzo in memoria. Invece, questo suona come una sorta di albero con puntatori e forse anche puntatori verso il codice che implementa le primitive. Non sono quindi sicuro che B :: Deparse possa far fronte a un albero di analisi generato da una diversa istanza perl. – otmar

+0

L'esame del codice Deparse mostra un altro problema: Depressa oeprated sugli oggetti perl che rappresentano il programma e non la memoria/flusso di byte che memorizza quel codice. – otmar

Problemi correlati