solicita - Kernel de Linux: Ejemplo de enlace de llamada al sistema
llamadas al sistema(< system calls>) (4)
Intento escribir un código de prueba simple como una demostración de enganchar la tabla de llamadas del sistema.
"sys_call_table" ya no se exporta en 2.6, así que solo estoy tomando la dirección del archivo System.map, y puedo ver que es correcta (mirando a través de la memoria en la dirección que encontré, puedo ver los punteros al llamadas al sistema).
Sin embargo, cuando trato de modificar esta tabla, el núcleo da un "Oops" con "no se puede manejar la solicitud de paginación del kernel en la dirección virtual c061e4f4" y la máquina se reinicia.
Esto es CentOS 5.4 ejecutando 2.6.18-164.10.1.el5. ¿Hay algún tipo de protección o solo tengo un error? Sé que viene con SELinux, y he intentado ponerlo en modo permisivo, pero no hace la 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;
}
Finalmente encontré la respuesta yo mismo.
http://www.linuxforums.org/forum/linux-kernel/133982-cannot-modify-sys_call_table.html
El kernel se modificó en algún momento para que la tabla de llamadas del sistema sea de solo lectura.
cypherpunk:
Incluso si es tarde, pero la Solución puede interesar a los demás también: En el archivo entry.S encontrará: Código:
.section .rodata,"a" #include "syscall_table_32.S"
sys_call_table -> ReadOnly Tienes que compilar el Kernel nuevo si quieres "hackear" con sys_call_table ...
El enlace también tiene un ejemplo de cómo cambiar la memoria para que se pueda escribir.
nasekomoe:
Hola a todos. Gracias por las respuestas Resolví el problema hace mucho tiempo modificando el acceso a las páginas de memoria. Implementé dos funciones que lo hacen para mi código de nivel superior:
#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
Aquí hay una versión modificada del código original que funciona para mí.
#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;
}
Gracias Stephen, tu investigación aquí fue útil para mí. Sin embargo, tuve algunos problemas, ya que estaba intentando esto en un kernel 2.6.32, y obteniendo WARNING: at arch/x86/mm/pageattr.c:877 change_page_attr_set_clr+0x343/0x530() (Not tainted)
seguido de un kernel OOPS acerca de no poder escribir en la dirección de memoria.
El comentario sobre la línea mencionada dice:
// People should not be passing in unaligned addresses
El siguiente código modificado funciona:
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 todavía no configura la página como de lectura / escritura en algunas situaciones. La función static_protections()
, que se llama dentro de set_memory_rw()
, elimina el indicador _PAGE_RW
si:
- Está en el área de BIOS
- La dirección está dentro de .rodata
- CONFIG_DEBUG_RODATA está configurado y el núcleo está configurado como de solo lectura
Descubrí esto después de la depuración porque todavía tengo "incapaz de manejar la solicitud de paginación del kernel" cuando trato de modificar la dirección de las funciones del kernel. Eventualmente pude resolver ese problema buscando la entrada de la tabla de la página para la dirección y configurándola manualmente para escritura. Afortunadamente, la función lookup_address()
se exporta en la versión 2.6.26+. Aquí está el código que escribí para hacer eso:
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, aunque la respuesta de Mark es técnicamente correcta, será un problema de caso cuando se ejecute dentro de Xen. Si desea deshabilitar la protección contra escritura, use las funciones de lectura / escritura cr0. Los macro de esta manera:
#define GPF_DISABLE write_cr0(read_cr0() & (~ 0x10000))
#define GPF_ENABLE write_cr0(read_cr0() | 0x10000)
Espero que esto ayude a cualquier otra persona que tropiece con esta pregunta.
Si está tratando con kernel 3.4 y posterior (también puede funcionar con kernels anteriores, no lo probé), recomendaría una forma más inteligente de adquirir la ubicación de la tabla callouts del sistema.
Por ejemplo
#include <linux/module.h>
#include <linux/kallsyms.h>
static unsigned long **p_sys_call_table;
/* Aquire system calls table address */
p_sys_call_table = (void *) kallsyms_lookup_name("sys_call_table");
Eso es. Sin direcciones, funciona bien con cada núcleo que he probado.
De la misma manera que puede usar una función Kernel no exportada desde su módulo:
static int (*ref_access_remote_vm)(struct mm_struct *mm, unsigned long addr,
void *buf, int len, int write);
ref_access_remote_vm = (void *)kallsyms_lookup_name("access_remote_vm");
¡Disfrutar!
Tenga en cuenta que lo siguiente también funcionará en lugar de usar change_page_attr y no se puede depreciar:
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));
}
}