2010-08-26 9 views
11

El servidor de un amigo (sí, realmente. No el mío) se rompió y descubrimos un binario de Perl ejecutando un código de bot. No pudimos encontrar el script en sí (probablemente evaluado como recibido a través de la red), pero logramos crear un volcado de núcleo del proceso de perl.Ingeniería inversa una secuencia de comandos Perl basada en un volcado del núcleo

La ejecución de cadenas en el núcleo nos dio algunos consejos (nombres de host, nombres de usuario/contraseñas), pero no el código fuente del script.

Nos gustaría saber qué es lo que el script era capaz de hacer, por lo que nos gustaría realizar una ingeniería inversa del código perl que se ejecutaba dentro de ese intérprete perl.

Buscando, lo más parecido a un compilador de perl que encontré es el módulo B :: Deparse que parece ser perfectamente adecuado para convertir el bytecode de los árboles de análisis en código legible.

Ahora, ¿cómo consigo que B :: Deparse funcione en un volcado de memoria? O, como alternativa, ¿cómo podría reiniciar el programa desde el núcleo, cargar B :: Deparse y ejecutarlo?

Cualquier idea es bienvenida.

Respuesta

7

ysth me preguntó en el IRC para comentar su pregunta. He hecho una pila completa de cosas que "desmontan" perl compilado y esas cosas (solo vea mi página de CPAN [http://search.cpan.org/~jjore]).

Perl compila su fuente a un árbol de OP* estructuras que ocasionalmente tienen punteros C a SV* que son valores perl. Su volcado de núcleo ahora tiene un montón de esos OP* y SV* escondidos.

El mejor mundo posible sería tener un módulo perl como B::Deparse para que usted pueda entender la información. Se obras mediante el uso de una interfaz de luz a perl memoria en las clases B::OP y B::SV (documentado en B, perlguts y perlhack).Esto no es realista para usted porque un objeto B::* es solo un puntero en la memoria con accesores para decodificar la estructura para nuestro uso . Considere:

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

que cuando el funcionamiento mostró que el B::PV objeto (es ISA B::SV) es verdaderamente simplemente una interfaz para la representación memoria de la cadena compilado 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'}; 

Sin embargo, esto implica que cualquier B::* utilizando el código debe estar efectivamente operar en memoria viva. Tye McQueen pensó que recordaba un depurador de C que podría revivir por completo un proceso de trabajo dado un volcado de memoria. Mi gdb no puedo. gdb puede permitirle volcar el contenido de sus estructuras OP* y SV*. Lo más probable es que simplemente lea las estructuras volcadas al interpretar la estructura de su programa. Si lo desea, puede usar gdb para volcar las estructuras, luego crear sintéticamente B::* objetos que se comportaron en la interfaz como si fueran normales y usan B::Deparse en eso. En la raíz, nuestro deparser y otras herramientas de depuración de errores están orientadas principalmente a objetos, por lo que podría simplemente "engañarlos" por creando un montón de clases y objetos falsos B::*.

Puede encontrar la lectura de la clase B :: Deparse coderef2text método instructivo. Acepta una referencia de función, lo arroja a un objeto B::CV , y lo utiliza como entrada para el método 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); 

Para introducciones más suave para OP* e ideas relacionadas, ver la actualizado PerlGuts Illustrated y Optree guts.

+0

Josh, gracias por la respuesta detallada. Esto está muy bien con lo que esperaba. Parece un proyecto para largas noches de invierno. – otmar

0

Bueno, undump convertirá ese volcado de núcleo en un ejecutable binario (si puede encontrar una versión que funcione). Debería poder cargarlo en perl y -MO=Deparse.

+2

Err, creo que hay un defecto allí; ¿cómo se carga un ejecutable binario en Perl? – ysth

+0

Tenía la impresión de que acababa de hacer 'perl a.out' uno de estos días siguiendo un tutorial de Par :: Packer, pero lo intenté ahora y no funcionó. –

2

Dudo que haya una herramienta por ahí que lo hace fuera de la caja, así que ...

  1. encontrar el código fuente de la versión de Perl que se estaba ejecutando. Esto debería ayudarlo a comprender el diseño de la memoria del intérprete de Perl. También te ayudará a descubrir si hay una manera de tomar un atajo aquí (por ejemplo, si el bytecode va precedido de un encabezado fácil de encontrar en la memoria o algo así).

  2. de carga hasta el binario volcado + núcleo en un depurador, probablemente GDB

  3. uso de la información en el código fuente de Perl para guiarle en convencer al depurador para escupir el código de bytes que le interesa.

Una vez que tenga el bytecode, B :: Deparse debería poder llevarlo a algo más legible.

+0

Sí, eso suena razonable. Sin embargo, puede haber una trampa: la documentación de perl se refiere al código de bytes como árbol de análisis. Esto sugiere que el byte-code no es una matriz de códigos de operación que es independiente de su dirección en la memoria. En cambio, esto suena como un tipo de árbol con punteros y tal vez incluso indicadores para primitivos implementadores de código. No estoy seguro de si B :: Deparse puede hacer frente a un árbol de análisis que fue generado por una instancia de Perl diferente. – otmar

+0

Al observar el código Deparse se muestra otro problema: Deparse oeprated en los objetos perl que representan el programa, y ​​no la memoria/byte-stream que almacena ese código. – otmar

Cuestiones relacionadas