Vexamos entón como cazar as funcións que crexamos susceptibles... digamos `strcpy` para proba-lo usaremos este programa:
#include <string.h> #include <stdlib.h> // Produce segfault void fail(){ char buff[40]; strcpy(NULL, buff); } int main(int argc, char **argv){ fail(); return 0; }
Ben, o seguinte é cazar a chamada a `strcpy`, para isto temos que preparar unha libraría compartida coa función, este sería o código:
#include <stdio.h> #include <stdlib.h> #include <string.h> // As funcións para busca-la función real de strcpy están aquí #define __USE_GNU #include <dlfcn.h> // A función a cazar char *strcpy (char *dest, const char *src){ // Facémos o que teñamos que facer fprintf(stderr, "strcpy(\"%s\", \"%s\")\n", dest, src); // Buscámos a función "orixinal" char *(*real_call)(char *, const char *) = dlsym(RTLD_NEXT, "strcpy"); // Exécutamos a dita función e recuperamos o resultado char *resultado = real_call(dest, src); // E devolvémo-lo resultado return resultado; }
Este programa compílase nun *.so cos flags `-fPIC -shared -ldl`
gcc strcpy.c -o strcpy.so -fPIC -shared -ldl
Para rematar, só temos que executalo facendo `preload` da libraría que acabamos de xerar:
LD_PRELOAD=`pwd`/strcpy.so ./fail
Nota: Tede en conta que hai que indicar a ruta completa no LD_PRELOAD (por iso o do `pwd`)
Só queda cazar o sinal, o código quedaría así:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> // As funcións para busca-la función real de strcpy están aquí #define __USE_GNU #include <dlfcn.h> #define BUFFER_SIZE 128 void show_backtrace(){ void *buffer[128]; char **symbols; int ptr_num = backtrace(buffer, BUFFER_SIZE); int i; symbols = backtrace_symbols(buffer, ptr_num); if (symbols == NULL){ perror("backtrace_symbols"); return; } for(i = 0; i < ptr_num; i++){ printf("%i:\t %s\n", i, symbols[i]); } free(symbols); } void segfault_handler(int signal){ printf("Faio de segmentación, amosando traza:\n\n"); show_backtrace(); abort(); } // A función a cazar char *strcpy (char *dest, const char *src){ // Cazamos a sinal e gardamos o manexador anterior void *previous_handler = signal(SIGSEGV, segfault_handler); // Buscámos a función "orixinal" char *(*real_call)(char *, const char *) = dlsym(RTLD_NEXT, "strcpy"); // Exécutamos a dita función e recuperamos o resultado char *resultado = real_call(dest, src); // Restablecemos o manexador signal(SIGSEGV, previous_handler); // E devolvémo-lo resultado return resultado; }
Se repetimos os pasos anteriores, acabamos con isto:
E xa está! por suposto o nome dos arquivos é completamente arbitrario e podemos meter todas as funcións que queiramos dentro.
ps: Sobre o de preparar as sinais e despois facer execve para carga-lo programa a memoria... nanai, pérdense os manexadores.
Saúdos
No hay comentarios:
Publicar un comentario