Detección de Procesos Ocultos con Taskverse y Análisis de Memoria del Kernel

El archivo /proc/kcore proporciona una interfaz de tipo ELF para navegar por la memoria del kernel, gestionada por operaciones como proc_kcore_operations y organizada mediante una lista enlazada kclist_head. Cada elemento de la lista representa un segmento de memoria, mapeado a secciones ejecutables, con un segmento de notas preliminar que contiene metadatos adicionales.

Para facilitar la comprensión, se presenta un ejemplo simplificado que ilustra el uso de /proc/kcore. Dado que este archivo carece de tablas de secciones y símbolos, se recurre a /proc/kallsyms para localizar direcciones de símbolos mediante búsqueda secuencial.

archivo_simbolos = fopen("/proc/kallsyms", "r");
if (!archivo_simbolos) {
    perror("Error al abrir /proc/kallsyms");
    return 0;
}

while (fscanf(archivo_simbolos, "%lx %c %s", &direccion_encontrada, &caracter_tipo, nombre_leido) == 3) {
    if (strcmp(nombre_leido, nombre_objetivo) == 0) {
        fclose(archivo_simbolos);
        return direccion_encontrada;
    }
}

fclose(archivo_simbolos);
return 0;

}


</div>Para gestionar la información de los segmentos de /proc/kcore, se utiliza una lista enlazada que almacena la dirección virtual, el desplazamiento en el archivo y el tamaño de cada segmento.

<div>```
struct segmento_kcore {
    struct segmento_kcore *siguiente;
    unsigned long direccion_virtual;
    unsigned long desplazamiento;
    size_t tamano;
};

struct segmento_kcore lista_principal;

void inicializar_lista_segmentos() {
    int descriptor_archivo = open("/proc/kcore", O_RDONLY);
    if (descriptor_archivo < 0) {
        perror("Error al abrir /proc/kcore");
        exit(EXIT_FAILURE);
    }

    Elf64_Ehdr encabezado_elf;
    read(descriptor_archivo, &encabezado_elf, sizeof(Elf64_Ehdr));

    lseek(descriptor_archivo, encabezado_elf.e_phoff, SEEK_SET);
    Elf64_Phdr tabla_programas[encabezado_elf.e_phnum];
    read(descriptor_archivo, tabla_programas, sizeof(Elf64_Phdr) * encabezado_elf.e_phnum);
    close(descriptor_archivo);

    for (int i = 0; i < encabezado_elf.e_phnum; i++) {
        struct segmento_kcore *nuevo_segmento = malloc(sizeof(struct segmento_kcore));
        nuevo_segmento->direccion_virtual = tabla_programas[i].p_vaddr;
        nuevo_segmento->desplazamiento = encabezado_elf.e_phoff + sizeof(Elf64_Phdr) * i;
        nuevo_segmento->tamano = tabla_programas[i].p_memsz;

        struct segmento_kcore *actual = &lista_principal;
        while (actual->siguiente && actual->siguiente->direccion_virtual < nuevo_segmento->direccion_virtual) {
            actual = actual->siguiente;
        }
        nuevo_segmento->siguiente = actual->siguiente;
        actual->siguiente = nuevo_segmento;
    }
}
while (segmento_actual && direccion_objetivo > segmento_actual->direccion_virtual + segmento_actual->tamano) {
    segmento_actual = segmento_actual->siguiente;
}

if (!segmento_actual || direccion_objetivo < segmento_actual->direccion_virtual ||
    direccion_objetivo + cantidad > segmento_actual->direccion_virtual + segmento_actual->tamano) {
    return NULL;
}

int descriptor_archivo = open("/proc/kcore", O_RDONLY);
if (descriptor_archivo < 0) {
    perror("Error al abrir /proc/kcore para lectura");
    return NULL;
}

unsigned long desplazamiento_lectura = segmento_actual->desplazamiento + (direccion_objetivo - segmento_actual->direccion_virtual);
lseek(descriptor_archivo, desplazamiento_lectura, SEEK_SET);

void *buffer = malloc(cantidad);
read(descriptor_archivo, buffer, cantidad);
close(descriptor_archivo);

return buffer;

}


</div>Un ejemplo de uso consiste en localizar el descriptor de procesos init\_task y acceder a su campo de estado, que indica si el proceso está en ejecución (valor 0) o suspendido.

<div>```
int main() {
    inicializar_lista_segmentos();

    unsigned long direccion_init_task = obtener_direccion_simbolo("init_task");
    if (direccion_init_task == 0) {
        fprintf(stderr, "Símbolo init_task no encontrado\n");
        return EXIT_FAILURE;
    }

    void *datos_descriptor = leer_region_memoria(direccion_init_task, sizeof(unsigned long));
    if (datos_descriptor) {
        long estado_proceso = *((long *)datos_descriptor);
        printf("Estado de init_task: %ld\n", estado_proceso);
        free(datos_descriptor);
    }

    return EXIT_SUCCESS;
}

La estructura ELF interna almacena el encabezado ELF, la tabla de programas y el contenido del segmento de texto. Al localizar la dirección de init_task, se traduce a una posición en el archivo usando kcore_translate_kernel, y luego se lee la estructura task_struct. Al iterar sobre la lista de tareas y comparar con los procesos visibles en /proc, se identifican discrepancias que revelan procesos ocultos.

Etiquetas: taskverse /proc/kcore detección de procesos ocultos memoria del kernel formato ELF

Publicado el 7-14 05:22