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.