Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Estimación de las posibilidades de inyección segmentada.
- * Copyright (C) 2012 Gonzalo J. Carracedo
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- */
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <elf.h>
- #include <stdint.h>
- #include <sys/mman.h>
- #include <stdlib.h>
- #define DEBUG(x) write (1, x, sizeof (x) - 1)
- /* Esta versión implementa mmap en vez de read, write o lseek. Es más
- rápido, más corto y funcionará mejor para nuestros propósitos */
- /* Los includes pueden llegar a ser muy molestos. */
- #define O_RDWR 02
- /* He utilizado el mismo marcador en ambos, la cadena "KPAX". KPAX es una
- cadena más o menos segura y es realmente complicada encontrarla por ca-
- sualidad en un binario (se puede hacer la prueba con un simple grep KPAX
- en todos los ficheros de /bin, /usr/bin, y /usr/lib, no hay una sola
- coincidencia) */
- #define BOTTOM_MARKER_LOW 0x504b
- #define BOTTOM_MARKER_HIGH 0x5841
- #define TOP_MARKER_LOW BOTTOM_MARKER_LOW
- #define TOP_MARKER_HIGH BOTTOM_MARKER_HIGH
- #define BOTTOM_MARKER 0x5841504b
- #define TOP_MARKER BOTTOM_MARKER
- /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
- /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
- sustituirá por la cadena "palabra" */
- #define _STRINGIFY(x) #x
- #define STRINGIFY(x) _STRINGIFY (x)
- /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
- el flag "a" le obliga a que la sección se cargue en memoria */
- asm (".section .code_bottom, \"xa\"");
- asm (".long " STRINGIFY (BOTTOM_MARKER));
- asm (".section .code_top, \"xa\"");
- asm (".long " STRINGIFY (TOP_MARKER));
- /* write la conservamos SÓLO para debug */
- int
- write (int fd, const void *buf, int size)
- {
- int ret;
- asm volatile ("xchg %%ebx, %%esi\n"
- "int $0x80\n"
- "xchg %%ebx, %%esi\n" : "=a" (ret) :
- "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
- return ret;
- }
- /*
- * Código copiado por las malas de la libc para la implementación de mmap.
- */
- void *
- mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset);
- int
- munmap (void *start, size_t length);
- asm (".section .text");
- asm (".global munmap");
- asm ("munmap:");
- asm (" mov %ebx,%edx");
- asm (" mov 0x8(%esp),%ecx");
- asm (" mov 0x4(%esp),%ebx");
- asm (" mov $0x5b,%eax");
- asm (" int $0x80");
- asm (" mov %edx,%ebx");
- asm (" ret");
- asm (".global mmap");
- asm ("mmap:");
- asm (" push %ebp");
- asm (" push %ebx");
- asm (" push %esi");
- asm (" push %edi");
- asm (" mov 0x14(%esp), %ebx");
- asm (" mov 0x18(%esp), %ecx");
- asm (" mov 0x1c(%esp), %edx");
- asm (" mov 0x20(%esp), %esi");
- asm (" mov 0x24(%esp), %edi");
- asm (" mov 0x28(%esp), %ebp");
- asm (" shr $0xc, %ebp"); /* El kernel se espera directamente los 12 bits de página */
- asm (" mov $0xc0, %eax");
- asm (" int $0x80");
- asm (" pop %edi");
- asm (" pop %esi");
- asm (" pop %ebx");
- asm (" pop %ebp");
- asm (" ret");
- static int
- open (const char *path, int mode)
- {
- int ret;
- asm volatile ("xchg %%ebx, %%esi\n"
- "int $0x80\n"
- "xchg %%ebx, %%esi\n" : "=a" (ret) :
- "a" (__NR_open), "S" (path), "c" (mode));
- return ret;
- }
- static int
- close (int fd)
- {
- int ret;
- asm volatile ("xchg %%ebx, %%esi\n"
- "int $0x80\n"
- "xchg %%ebx, %%esi\n" : "=a" (ret) :
- "a" (__NR_close), "S" (fd));
- return ret;
- }
- #define GET_FILE_SIZE(fd, size) \
- asm volatile ("xchg %%ebx, %%esi\n" \
- "int $0x80\n" \
- "xchg %%ebx, %%esi\n" : "=a" (size) : \
- "a" (__NR_lseek), "S" (fd), "c" (0), "d" (2));
- /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
- muy útil para depurar. */
- void
- long2hex (unsigned int number, char *buf)
- {
- int i;
- char hexa[] = "0123456789abcdef";
- buf[8] = 0;
- for (i = 0; i < 8; i++)
- {
- buf[7 - i] = hexa[number & 0xf];
- number >>= 4;
- }
- }
- /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
- del %eip mediante un call (ya que no podemos calcular las direcciones de
- nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
- hacia arriba o hacia abajo hasta encontrarlos */
- void
- get_code_limits (uint32_t *bottom, uint32_t *top)
- {
- int i;
- uint32_t this_addr;
- /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
- de código en la que me hallo. El popl %0 saca la dirección de retorno
- que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
- inline assembly de GCC */
- /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
- que llamé 1 y que está hacia adelante. Esto de utilizar números para
- etiquetas se suele hacer mucho cuando la etiqueta hace referencia
- a un cacho de código que no está especialmente diferenciado del resto. */
- asm volatile ("call 1f\n"
- "1:\n"
- "popl %0\n" : "=g" (this_addr));
- /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
- últimos bits a cero. */
- this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
- this_addr = this_addr & 0xfffffffc que es un and
- en binario con todo 1 menos los dos últimos bits,
- esto fuerza a que el número esté alineado a 4. */
- /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
- nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
- dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
- El marcador está hacia "atrás", o sea que recorreremos la memoria
- restando. */
- for (i = this_addr; ; i -= 4)
- {
- if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
- if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
- {
- *bottom = i;
- break;
- }
- }
- /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
- nuestra memoria hacia adelante, sumando. */
- for (i = this_addr; ; i += 4)
- {
- if (*(uint16_t *) i == TOP_MARKER_LOW)
- if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
- {
- *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
- 4 bytes) también pertenece al código. */
- break;
- }
- }
- }
- /* Equivalente a map_elf, pero usando mmap */
- void *
- map_elf (const char *path, size_t *size)
- {
- int fd;
- int this_size;
- void *map;
- Elf32_Ehdr *Ehdr;
- if ((fd = open (path, O_RDWR)) < 0)
- return NULL; /* Error al abrir */
- GET_FILE_SIZE (fd, this_size);
- if (this_size < 0)
- return NULL;
- *size = this_size;
- map = mmap (NULL, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
- if ((uint32_t) map & 0xfff)
- return NULL;
- Ehdr = (Elf32_Ehdr *) map;
- if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
- Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
- Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
- Ehdr->e_ident[EI_MAG3] != ELFMAG3)
- {
- munmap (map, *size);
- return NULL; /* Números mágicos incorrectos */
- }
- if (Ehdr->e_ident[EI_CLASS] != ELFCLASS32)
- {
- munmap (map, *size);
- return NULL; /* El ELF no es de 32 bits */
- }
- if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
- {
- munmap (map, *size);
- return NULL; /* El ELF no es little endian */
- }
- if (Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
- {
- munmap (map, *size);
- return NULL; /* El ELF no es ni un ejecutable ni una biblioteca */
- }
- return map;
- }
- /* Macro para alinear a tamaños */
- #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
- int
- get_alloc_info (void *base, size_t *gap_bytes, int *nop_bytes, int *nop_chunks)
- {
- int i;
- int code_idx;
- size_t gap_size;
- unsigned char *text;
- int state = 0;
- int stripsize = 0;
- off_t gap_start, gap_start_relative;
- Elf32_Ehdr *Ehdr;
- Elf32_Phdr *Phdr;
- code_idx = -1;
- Ehdr = (Elf32_Ehdr *) base;
- Phdr = (Elf32_Phdr *) (base + Ehdr->e_phoff);
- for (i = 0; i < Ehdr->e_phnum; i++)
- {
- if (Phdr[i].p_type == PT_LOAD)
- {
- if (Phdr[i].p_flags & PF_X)
- {
- if (code_idx != -1) /* ¿Dos segmentos de código? dew */
- return -1;
- code_idx = i;
- }
- }
- }
- if (code_idx == -1)
- return -1;
- gap_start_relative = ALIGN (Phdr[code_idx].p_filesz, 4); /* Comienzo relativo al segmentp */
- gap_start = Phdr[code_idx].p_offset + gap_start_relative; /* Comienzo real */
- gap_size = 4096 - (gap_start & 0xfff); /* Tamaño */
- for (i = 0; i < Ehdr->e_phnum; i++)
- {
- /* ¿Está a caballo entre otro segmento? */
- if (Phdr[i].p_type == PT_LOAD)
- if (gap_start <= Phdr[i].p_offset && Phdr[i].p_offset < gap_start + gap_size)
- gap_size = Phdr[i].p_offset - gap_start; /* Corregimos */
- }
- *gap_bytes = gap_size;
- text = (unsigned char *) (base + Phdr[code_idx].p_offset);
- *nop_bytes = 0;
- *nop_chunks = 0;
- for (i = 0; i < Phdr[code_idx].p_filesz; i++)
- {
- if (!state)
- {
- if (text[i] == 0x90) /* RET */
- {
- stripsize = 1;
- state++;
- }
- }
- else
- {
- if (text[i] == 0x90)
- stripsize++;
- else
- {
- if (stripsize > 4)
- {
- (*nop_bytes) += stripsize;
- (*nop_chunks)++;
- }
- state--;
- }
- }
- }
- return 0;
- }
- /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
- void
- _start (void)
- {
- char buffer[9];
- size_t victim_size;
- void* victim_base;
- size_t code_size;
- int gap, nops, chunks;
- uint32_t limit_bottom;
- uint32_t limit_top;
- get_code_limits (&limit_bottom, &limit_top);
- code_size = limit_top - limit_bottom;
- if ((victim_base = map_elf ("victim", &victim_size)) == NULL)
- {
- DEBUG ("Imposible abrir victim\n");
- for (;;);
- }
- if (get_alloc_info (victim_base, &gap, &nops, &chunks) == -1)
- {
- DEBUG ("El binario tiene un segmento de texto extraño.\n");
- for (;;);
- }
- DEBUG ("Tamaño del código a inyectar: ");
- long2hex (code_size, buffer);
- buffer[8] = 10;
- write (1, buffer, 9);
- DEBUG ("Bytes en la brecha: ");
- long2hex (gap, buffer);
- buffer[8] = 10;
- write (1, buffer, 9);
- DEBUG ("Nop bytes: ");
- long2hex (nops, buffer);
- buffer[8] = 10;
- write (1, buffer, 9);
- DEBUG ("Divisiones: ");
- long2hex (chunks, buffer);
- buffer[8] = 10;
- write (1, buffer, 9);
- if (gap < chunks * 4)
- {
- DEBUG ("Los offsets no caben en la brecha\n");
- if (code_size < gap + nops - 4 * chunks)
- DEBUG ("Sin embargo, si incluimos la información en los trozos, PUEDE hacerse\n");
- else
- DEBUG ("Y tampoco caben en los trozos :( la infección es imposible.\n");
- }
- else
- {
- DEBUG ("Tamaño aprovechable de la brecha (incluyendo offsets): ");
- long2hex (gap - chunks * 4, buffer);
- buffer[8] = 10;
- write (1, buffer, 9);
- if (code_size < (gap - chunks * 4 + nops))
- DEBUG ("La infección PUEDE hacerse\n");
- else
- DEBUG ("La infección no puede hacerse :(\n");
- }
- for (;;);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement