Los que me leéis habitualmente probablemente hayais visto los artículos que escribí sobre ingeniería inversa en C, o explotación de buffers overflows. Son temas que me llaman la atención, hoy quiero hablaros de dos secciones de la tabla de secciones en C llamadas .dtors y .ctors, creada por los binarios compilados con el compilador de GNU.

Estas secciones se crean para los destructores y constructores, respectivamente. Los constructores se llaman justo antes de ejecutar la función main() y los destructores se llaman justo antes de que main() finalize con la llamada al sistema exit.

Veamos un ejemplo para aclarar su funcionamiento:



#include <stdio.h>
#include <stdlib.h>

static void
miConstructor(void) __attribute__ ((constructor));
static void
miDestructor(void) __attribute__ ((destructor));

int
main(void) {
 printf("En main() \n");
    return EXIT_SUCCESS;
}

void
miConstructor(void) {
  printf("En el constructor\n");
}

void
miDestructor(void) {
    printf("En el destructor\n");
}

La salida de este programa será:

En el constructor
En main()
En el destructor

Al declarar miConstructor como constructor la función se llama antes de ejecutar main, lo mismo pasa para miDescructor, pero en este caso se la llamará justo antes de salir de main.

La forma de controrlar la ejecución de ambas funciones es mediante las secciones .dtors y .ctors. Estas secciones son un array de direcciones de 32 bits que terminan con una dirección nula (la dirección 0x00000000). Así se determina donde empieza y donde acaba el array. Parecido a lo que hice en el artículo Ocultar archivos dentro de una imagen donde delimité el nombre del archivo por un pixel en blanco para saber que había llegado al final del nombre. De modo que entre el inicio de la sección .dtors y .ctors y la dirección nula se encuentran las funciones declaradas como destructores o constructores, respectivamente.

Para localizar las funciones hay que usar el programa nm, y objdump para encontrar las dos secciones.

Sin embargo, mientras escribía este artículo me dí cuenta de que ya no existen dichas secciones, han sido reemplazadas por .init_array/.fini_array. Del mismo modo pueden verse usando nm, y objdump. Empecemos con nm:

$ nm dtors
080495f0 d _DYNAMIC
080496e4 d _GLOBAL_OFFSET_TABLE_
080484dc R _IO_stdin_used
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
         w _Jv_RegisterClasses
080485d8 r __FRAME_END__
080495ec d __JCR_END__
080495ec d __JCR_LIST__
08049704 D __TMC_END__
08049704 A __bss_start
080496fc D __data_start
080483c0 t __do_global_dtors_aux
080495e4 t __do_global_dtors_aux_fini_array_entry
08049700 D __dso_handle
080495dc t __frame_dummy_init_array_entry
         w __gmon_start__
080484ba T __i686.get_pc_thunk.bx
080495e4 t __init_array_end
080495dc t __init_array_start
08048450 T __libc_csu_fini
08048460 T __libc_csu_init
         U __libc_start_main@@GLIBC_2.0
08049704 A _edata
08049708 A _end
080484c0 T _fini
080484d8 R _fp_hw
080482b8 T _init
08048320 T _start
08049704 b completed.5730
080496fc W data_start
08048350 t deregister_tm_clones
080483e0 t frame_dummy
0804840c T main
08048428 t miConstructor
0804843c t miDestructor
         U puts@@GLIBC_2.0
08048380 t register_tm_clones

En la salida se muestran como _init y _fini. También es posible obtener más información sobre las secciones de la tabla con objdumb:

$ objdump -h ./dtors

./dtors:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .interp       00000013  08048134  08048134  00000134  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  08048148  08048148  00000148  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  08048168  08048168  00000168  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .hash         00000028  0804818c  0804818c  0000018c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .gnu.hash     00000020  080481b4  080481b4  000001b4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       00000050  080481d4  080481d4  000001d4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .dynstr       0000004a  08048224  08048224  00000224  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version  0000000a  0804826e  0804826e  0000026e  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .gnu.version_r 00000020  08048278  08048278  00000278  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rel.dyn      00000008  08048298  08048298  00000298  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .rel.plt      00000018  080482a0  080482a0  000002a0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .init         00000026  080482b8  080482b8  000002b8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt          00000040  080482e0  080482e0  000002e0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .text         000001a0  08048320  08048320  00000320  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .fini         00000017  080484c0  080484c0  000004c0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .rodata       00000036  080484d8  080484d8  000004d8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame_hdr 0000002c  08048510  08048510  00000510  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .eh_frame     000000a0  0804853c  0804853c  0000053c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .init_array   00000008  080495dc  080495dc  000005dc  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .fini_array   00000008  080495e4  080495e4  000005e4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 20 .jcr          00000004  080495ec  080495ec  000005ec  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 21 .dynamic      000000f0  080495f0  080495f0  000005f0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got          00000004  080496e0  080496e0  000006e0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 23 .got.plt      00000018  080496e4  080496e4  000006e4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 24 .data         00000008  080496fc  080496fc  000006fc  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 25 .bss          00000004  08049704  08049704  00000704  2**2
                  ALLOC
 26 .comment      00000038  00000000  00000000  00000704  2**0
                  CONTENTS, READONLY

El contenido que nos interesa es:

18 .init_array   00000008  080495dc  080495dc  000005dc  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .fini_array   00000008  080495e4  080495e4  000005e4  2**2
                  CONTENTS, ALLOC, LOAD, DATA

Esta vez hemos obtenido más información, sabemos que ambas secciones ocupan 8 Bytes y se puede escribir en ellas, ya que no tienen la etiqueta READONLY. Para examinar el contenido basta con ejecutar:

$ objdump -s -j .fini_array ./dtors
Contents of section .fini_array:
 80495e4 c0830408 3c840408

La primera dirección apunta a la tabla de desplazamiento global (_GLOBAL_OFFSET_TABLE_) y la segunda a __do_global_dtors_aux.
La última dirección (3c840408) corresponde con la dirección de la función miDestructor, pero en little-endian (0804843c). Al poder modificar dicha tabla, sería posible tomar el control del programa explotando alguna vulnerabilidad y obtener una shell con permisos de root. El propósito inicial del artículo era mostar cómo explotar dicha vulnerabilidad, pero al no existir la sección .ctors y .dtors no va a ser posible, ya que he estado trasteando un poco con estas secciones nuevas y no he conseguido nada.

Aún así, espero que os haya resultado útil para aprender sobre los constructores y destructores en C, y hayáis conocido un poco más a fondo la estructura de un ejecutable.

http://www.exploit-db.com/papers/13234/

Referencias

Replace .ctors/.dtors with .init_array/.fini_array »» gcc.gnu.org
Abusing .CTORS and .DTORS For FUN and PROFIT »» exploit-db.com

Índice

</stdlib.h></stdio.h>