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