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