48 votos

El Kernel de Linux: Sistema de llamada de enganche ejemplo

Estoy tratando de escribir algunas simples código de prueba como una demostración de enlazar el sistema de llamada de la tabla.

"sys_call_table" ya no es más exportado en 2.6, así que me estoy tomando la dirección del Sistema.archivo de mapa, y puedo ver que es correcto (Mirando a través de la memoria en la dirección que he encontrado, puedo ver los punteros a las llamadas del sistema).

Sin embargo, cuando trato de modificar esta tabla, el kernel le da un "Oops" con "incapaz de manejar núcleo de paginación solicitud a la dirección virtual c061e4f4" y la máquina se reinicia.

Este es CentOS 5.4 ejecutar 2.6.18-164.10.1.el5. ¿Hay algún tipo de protección o sólo debo tener un error? Sé que viene con SELinux, y he intentado ponerlo en modo permisivo, pero no hacer una diferencia

Aquí está mi código:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>

void **sys_call_table;

asmlinkage int (*original_call) (const char*, int, int);

asmlinkage int our_sys_open(const char* file, int flags, int mode)
{
   printk("A file was opened\n");
   return original_call(file, flags, mode);
}

int init_module()
{
    // sys_call_table address in System.map
    sys_call_table = (void*)0xc061e4e0;
    original_call = sys_call_table[__NR_open];

    // Hook: Crashes here
    sys_call_table[__NR_open] = our_sys_open;
}

void cleanup_module()
{
   // Restore the original call
   sys_call_table[__NR_open] = original_call;
}

38voto

Stephen Pape Puntos 2130

Finalmente encontré la respuesta a mí mismo.

http://www.linuxforums.org/forum/linux-kernel/133982-cannot-modify-sys_call_table.html

El núcleo fue cambiado en algún momento para que el sistema de llamada de la tabla es de sólo lectura.

cypherpunk:

Incluso si es tarde, pero la Solución puede interesar a otros: En el de entrada.S archivo encontrará: Código:

.section .rodata,"a"
#include "syscall_table_32.S"

sys_call_table -> lectura-sólo tienes Que compilar el Kernel de nuevo si quieres "hack" alrededor con sys_call_table...

El enlace también tiene un ejemplo de cambio de la memoria para ser modificable.

nasekomoe:

Hola a todos. Gracias por las respuestas. Yo resuelto el problema hace mucho tiempo por modificación de acceso a las páginas de memoria. Yo se han implementado dos funciones que hacen para mi el nivel superior de código:

#include <asm/cacheflush.h>
#ifdef KERN_2_6_24
#include <asm/semaphore.h>
int set_page_rw(long unsigned int _addr)
{
    struct page *pg;
    pgprot_t prot;
    pg = virt_to_page(_addr);
    prot.pgprot = VM_READ | VM_WRITE;
    return change_page_attr(pg, 1, prot);
}

int set_page_ro(long unsigned int _addr)
{
    struct page *pg;
    pgprot_t prot;
    pg = virt_to_page(_addr);
    prot.pgprot = VM_READ;
    return change_page_attr(pg, 1, prot);
}

#else
#include <linux/semaphore.h>
int set_page_rw(long unsigned int _addr)
{
    return set_memory_rw(_addr, 1);
}

int set_page_ro(long unsigned int _addr)
{
    return set_memory_ro(_addr, 1);
}

#endif // KERN_2_6_24

He aquí una versión modificada de la versión original del código que me funciona.

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <asm/semaphore.h>
#include <asm/cacheflush.h>

void **sys_call_table;

asmlinkage int (*original_call) (const char*, int, int);

asmlinkage int our_sys_open(const char* file, int flags, int mode)
{
   printk("A file was opened\n");
   return original_call(file, flags, mode);
}

int set_page_rw(long unsigned int _addr)
{
   struct page *pg;
   pgprot_t prot;
   pg = virt_to_page(_addr);
   prot.pgprot = VM_READ | VM_WRITE;
   return change_page_attr(pg, 1, prot);
}

int init_module()
{
    // sys_call_table address in System.map
    sys_call_table = (void*)0xc061e4e0;
    original_call = sys_call_table[__NR_open];

    set_page_rw(sys_call_table);
    sys_call_table[__NR_open] = our_sys_open;
}

void cleanup_module()
{
   // Restore the original call
   sys_call_table[__NR_open] = original_call;
}

20voto

Corey Henderson Puntos 3487

Gracias Esteban, su investigación aquí fue útil para mí. Tuve un par de problemas, a pesar de que, como yo estaba tratando de esto en una imagen del kernel 2.6.32, y consiguiendo WARNING: at arch/x86/mm/pageattr.c:877 change_page_attr_set_clr+0x343/0x530() (Not tainted) , seguido por el kernel OOPS acerca de no ser capaz de escribir a la dirección de memoria.

El comentario de arriba de la mencionada línea de los estados:

// People should not be passing in unaligned addresses

El siguiente código modificado de las obras:

int set_page_rw(long unsigned int _addr)
{
    return set_memory_rw(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
}

int set_page_ro(long unsigned int _addr)
{
    return set_memory_ro(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
}

Tenga en cuenta que esto no establecer realmente la página como de lectura/escritura en algunas situaciones. El static_protections() función, que se llama dentro de la set_memory_rw(), elimina la _PAGE_RW bandera si:

  • Es en el área de BIOS
  • La dirección es en el interior .rodata
  • CONFIG_DEBUG_RODATA y el kernel es el conjunto de sólo lectura

He encontrado esto después de la depuración por qué todavía tengo "incapaz de manejar núcleo de paginación petición" al intentar modificar la dirección de las funciones del núcleo. Me fue finalmente capaz de resolver el problema mediante la búsqueda de la tabla de la página de entrada para la dirección de mi mismo y de forma manual la configuración de permisos de escritura. Afortunadamente, el lookup_address() función se exporta en la versión 2.6.26+. Aquí está el código que escribí para hacerlo:

void set_addr_rw(unsigned long addr) {

    unsigned int level;
    pte_t *pte = lookup_address(addr, &level);

    if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;

}

void set_addr_ro(unsigned long addr) {

    unsigned int level;
    pte_t *pte = lookup_address(addr, &level);

    pte->pte = pte->pte &~_PAGE_RW;

}

Finalmente, mientras que la Marca de la respuesta es técnicamente correcta, que va a caso el problema cuando se corrió dentro de Xen. Si desea desactivar la protección contra escritura, el uso de la lectura/escritura de cr0 funciones. Yo macro de la siguiente forma:

#define GPF_DISABLE write_cr0(read_cr0() & (~ 0x10000))
#define GPF_ENABLE write_cr0(read_cr0() | 0x10000)

Espero que esto ayuda a cualquier persona que se tropieza a esta pregunta.

14voto

mark Puntos 121

Tenga en cuenta que los siguientes también trabajo en lugar de utilizar change_page_attr y no puede ser depreciado:

static void disable_page_protection(void) {

    unsigned long value;
    asm volatile("mov %%cr0,%0" : "=r" (value));
    if (value & 0x00010000) {
            value &= ~0x00010000;
            asm volatile("mov %0,%%cr0": : "r" (value));
    }
}

static void enable_page_protection(void) {

    unsigned long value;
    asm volatile("mov %%cr0,%0" : "=r" (value));
    if (!(value & 0x00010000)) {
            value |= 0x00010000;
            asm volatile("mov %0,%%cr0": : "r" (value));
    }
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X