LINUX-BG   Адрес : http://www.linux-bg.org
(почти)Пълно ръководство за модулите в Linux ядрото [част 1]
От: pragmatic / THC
Публикувана на: 21-05-2001
Адрес на статията: http://www.linux-bg.org/cgi-bin/y/index.pl?page=article&id=advices&key=330146683
(nearly) Complete Linux Loadable Kernel Modules

-the definitive guide for hackers, virus coders and system administrators-

Преведено от: Андрю Иванов. Оригиналното ръководство може да откриете на този адрес

СЪДЪРЖАНИЕ

Въведение

I. Основи
1. Какво представляват LKM
2. Какво представляват системните повиквания (systemcalls)
3. Какво представлява Kernel-Symbol-Table (таблица със символите на ядрото)
 

Въведение

Ползата от Linux в света на сървърите нараства всяка секунда. Така че да хакнеш Linux става все по-интересно с всеки изминал ден. Една от най-добрите техники за атака на Linux система е чрез използване на kernel код т.е. програмен код за ядрото на операционната система. Благодарение на функцията наречена "Loadable Kernel Modules" (Заредими Модули на Ядрото - LKM) е възможно да се напише код, работещ в рамките на ядрото, което ни позволява да достигнем много чувствителни части на операционната система. Преди време съществуваха някои текстове и файлове, засягащи LKM хакването (Phrack, например), които бяха доста добри. Те They представиха нови идеи, нови методи и завършени LKMs, правещи всичко за което един хакер някога е мечтал. Също така и някои публични дискусии през 1998 (новинарски групи, пощенски списъци) бяха много интересни.
И така, защо да пиша отново за LKMs? Ами, има няколко причини:
  • предишните текстове, понякога, не даваха добри обяснения за начинаещите; този текст има много обширна част посветена на основите, помагаща на начинаещите да разберат основните концепции. Срещал съм много хора, използващи добри снифъри и тям подобни без дори да разбират как тези "играчки" работят. Включих много сорс код в този файл с много коментари, просто за да помогна на начинаещите, които знаят, че хакерството е нещо повече от това да опустошаваш някакви си мрежи !
  • всеки публикуван текст се концентрираше върху определена тема, не съществуваше завършено ръководство за хакери, което да засяга LKMs. Този текст ще засегне почти всички аспекти от злоупотребата с ядрото (дори вирусите)
  • този текст беше написан от гледната точка на хакер / създател на вируси, но ще е от полза и за администраторите и обикновените разработчици, занимаващи се с ядрото
  • предишните текстове демонстрираха най-важните изгоди и методи на злоупотреба с LKMs, но съществуват неща, за които все още не сме и чували. Този текст ще демонстрира някои нови идеи (нищо изцяло ново, но все пак няща, които могат да ни помогнат)
  • този текст ще покаже някои идеи за прост начин за предпазване от LKM атаки
  • този текст ще демонстрира и как да бъдат преодоляни LKM защитите чрез използване на методи като Runtime Kernel Patching ("закърпване" на ядрото в реално време)
Моля, запомнете, че новите идеи за изпълнени под формата на модули-прототипи (само за демонстрационни цели), които трябва да бъдат подобрени, за да будат използвани в реалността.
Основната мотивация на този текст е да даде на всеки един голям текст, покриващ целия проблем LKM. В приложение A давам няколко съществуващи LKMs плюс кратко описание на тяхната работа (за начинаещи) и начини за тяхното използване.
Целият текст (с изключение на част V) се базира на Linux 2.0.x машина (x86). Тествал съм всички програми и фрагменти код. Linux системата трябва да поддържа LKM, за да използвате повечето примерни кодове в този текст. Само част IV ще демонстрира някои сорсове, които работят без вградена поддръжка на LKM. Повечето исеи в този текст че работят и на 2.2.x системи (вероятно ще трябва да направите някои минимално корекции); но спомнете си, че ядро 2.2.x тъкмо беше излязло (1/99) и повечето linux дистрибуции все още използват 2.0.x (Redhat, SuSE, Caldera, ...). През април някои дистрибутори, като SuSE ще покажат техните версии с ядро 2.2.x; така че няма да имате нужда да знаете как да хакнете ядро 2.2.x към настоящия момент. Добрите администратори също ще изчакат няколко месеца, за да получат по-надеждно 2.2.x ядро. [Забележка : Повечето системи просто не се нуждаят от ядро 2.2.x, така че те ще продължат да използват 2.0.x].
Този текст има специален раздел, занимаващ се с LKMs, помагащи на администраторите да обезопасят системата. Вие (хакерите) трябва също да прочетете този раздел, вие трябва да знаете всичко, което админите знаят и дори повече. Ще получите някои добри идеи от този раздел, които могат да ви помогнат да разработите по-напреднали 'хекерски LKMs'. Просто прочетете целия текст !
И, моля ви, запомнете : Този текст беше написан единствено за учебни цели. Всяко противозаконно действие основано на този текст си е лично ваш проблем.

I. Основи

1. Какво представляват LKM

LKM - това са Loadable Kernel Modules (Заредими Модули на Ядрото. Те се използват от Linux ядрото за повишаване на функционалността му. Ползите от LKM : Tе могат да се зареждат динамично; не се налага прекомпилиране на цялото ядро. Поради тези си свойства, LKM се използват често за драйвери за специфични устройства (или файлови системи), като например звукови карти и подобни.
Всеки LKM се състои от две основни функции (минимум) :
int init_module(void) /*използва се за всичко свързано с инициализацията*/
 {
 
 ...
 }
 
 void cleanup_module(void) /*използва се за 'чисто' спиране на модула*/
 {
 ...
 }
 
 
 Зареждане на модул - обикновено извършвано от root - става чрез командата:
 
 
# insmod module.o
 
 
 Тази команда предизвиква извършването на следните действия от системата :
  • Зареждане на обектния файл (в случая module.o)
  • Извиква системно повикване create_module (за системни повиквания -> виж I.2) за презареждане на паметта
  • неуточнените препратки (references) се превръщат в Kernel-Symbol-и чрез системно повикване get_kernel_syms
  • след това се използва системно повикване init_module за инсталиране на LKM-а -> изпълнявайки int init_module(void) и т.н.
Kernel-Symbol-ите са обяснени в I.3 (Kernel-Symbol-Table).
И така, струва ми се, че вече може да напишем първият си малък LKM, просто за да демонстрираме основните принципи на работа:
#define MODULE
 #include <Linux/module.h>
 
 int init_module(void)
 {
  printk("<1>Hello World\n");
  return 0;
 }
 
 void cleanup_module(void)
 {
  printk("<1>Bye, Bye");
 }
 
 
 Може би се учудвате, че използвах printk(...), а не printf(...). Е добре,
 програмирането на ниво ядро
 е напълно различно от програмирането на потребителско ниво !
 
 Разполагате с много ограничен набор от команди (виж I.6). С тези команди 
 не можете да направите кой знае какво, така че ще се научите как да използвате много от 
 функциите, които познавате от вашите потребителски програми; те ще ви помагат да хакнете ядрото.
 Само бъдете търпеливи, първо трябва да свършим още нещо...
 
 Примерният LKM, даден по-горе може лесно да бъде компилиран по следният начин:
# gcc -c -O3 helloworld.c
 # insmod helloworld.o
 
 
 Добре, нашият модул е зареден и ни показа един известен текст. Сега можете да пробвате някои 
 команди, които ще ви показат, че LKM-а наистина се разполага в рамките на ядрото. 
 
 
# lsmod
 Module         Pages    Used by
 helloworld         1          0
 
 
 Тази команда изчита информацията в /proc/modules, за да ви покаже кои
 модули са заредени в момента. 'Страниците' (pages) дават информация за паметта (колко страници
 от паметта заема модула); Полето 'използван от' (used by) ни дава информация колко често модулът
 се използва от Системата (брой обръщения). Модулът може да бъде отстранен, само когато 
 този брояч показва нула; след като проверите това, можете да премахнете модула със
 
 
# rmmod helloworld
 
 
 Е, това беше нашата първа малка стъпка (наистина мъничка) към злоупотребите с LKMs. Винаги
 съм сравнявал тези LKMs със старите TSR програми под DOS (да, знам, съществуват много разлики),
 те бяха нашата вратичка за пребиваване в паметта и прихващане на всяко прекъсване, което 
 пожелаем. Microsoftския WIN 9x има нещо наречено VxD, което също е много подобно на
 LKMs (и тук има много разлики). Най-интересното при тези резидентни програми
 е възможността да прихванеме системните функции, наричани 'системни повиквания' (systemcalls) 
 в света на Linux
 
 
 

2. Какво представляват системните повиквания (systemcalls)

Надявам се знаете, че всяка ОС има функции, вградени в ядрото й, които се използват за извършването на всяка една операция в тази система.
Функциите, които Linux използва се наричат системни повиквания (systemcalls). Те представляват преход от потребителското пространство към ниво 'ядро'. Отварянето на фаил в потребителското пространство е представено от системно повикване sys_open на ниво ядро. За изчерпателен списък на системните повиквания (systemcalls), които са достъпни за вашата система погледнете в /usr/include/sys/syscall.h. Този списък представлява моят syscall.h
#ifndef _SYS_SYSCALL_H
 #define _SYS_SYSCALL_H
 
 #define SYS_setup               0 /* Използва се само от init, за да се 'подкара' системата. */
 #define SYS_exit                1
 #define SYS_fork                2
 #define SYS_read                3
 #define SYS_write               4
 #define SYS_open                5
 #define SYS_close               6
 #define SYS_waitpid             7
 #define SYS_creat               8
 #define SYS_link                9
 #define SYS_unlink              10
 #define SYS_execve              11
 #define SYS_chdir               12
 #define SYS_time                13
 #define SYS_prev_mknod          14
 #define SYS_chmod               15
 #define SYS_chown               16
 #define SYS_break               17
 #define SYS_oldstat             18
 #define SYS_lseek               19
 #define SYS_getpid              20
 #define SYS_mount               21
 #define SYS_umount              22
 #define SYS_setuid              23
 #define SYS_getuid              24
 #define SYS_stime               25
 #define SYS_ptrace              26
 #define SYS_alarm               27
 #define SYS_oldfstat            28
 #define SYS_pause               29
 #define SYS_utime               30
 #define SYS_stty                31
 #define SYS_gtty                32
 #define SYS_access              33
 #define SYS_nice                34
 #define SYS_ftime               35
 #define SYS_sync                36
 #define SYS_kill                37
 #define SYS_rename              38
 #define SYS_mkdir               39
 #define SYS_rmdir               40
 #define SYS_dup                 41
 #define SYS_pipe                42
 #define SYS_times               43
 #define SYS_prof                44
 #define SYS_brk                 45
 #define SYS_setgid              46
 #define SYS_getgid              47
 #define SYS_signal              48
 #define SYS_geteuid             49
 #define SYS_getegid             50
 #define SYS_acct                51
 #define SYS_phys                52
 #define SYS_lock                53
 #define SYS_ioctl               54
 #define SYS_fcntl               55
 #define SYS_mpx                 56
 #define SYS_setpgid             57
 #define SYS_ulimit              58
 #define SYS_oldolduname         59
 #define SYS_umask               60
 #define SYS_chroot              61
 #define SYS_prev_ustat          62
 #define SYS_dup2                63
 #define SYS_getppid             64
 #define SYS_getpgrp             65
 #define SYS_setsid              66
 #define SYS_sigaction           67
 #define SYS_siggetmask          68
 #define SYS_sigsetmask          69
 #define SYS_setreuid            70
 #define SYS_setregid            71
 #define SYS_sigsuspend          72
 #define SYS_sigpending          73
 #define SYS_sethostname         74
 #define SYS_setrlimit           75
 #define SYS_getrlimit           76
 #define SYS_getrusage           77
 #define SYS_gettimeofday        78
 #define SYS_settimeofday        79
 #define SYS_getgroups           80
 #define SYS_setgroups           81
 #define SYS_select              82
 #define SYS_symlink             83
 #define SYS_oldlstat            84
 #define SYS_readlink            85
 #define SYS_uselib              86
 #define SYS_swapon              87
 #define SYS_reboot              88
 #define SYS_readdir             89
 #define SYS_mmap                90
 #define SYS_munmap              91
 #define SYS_truncate            92
 #define SYS_ftruncate           93
 #define SYS_fchmod              94
 #define SYS_fchown              95
 #define SYS_getpriority         96
 #define SYS_setpriority         97
 #define SYS_profil              98
 #define SYS_statfs              99
 #define SYS_fstatfs             100
 #define SYS_ioperm              101
 #define SYS_socketcall          102
 #define SYS_klog                103
 #define SYS_setitimer           104
 #define SYS_getitimer           105
 #define SYS_prev_stat           106
 #define SYS_prev_lstat          107
 #define SYS_prev_fstat          108
 #define SYS_olduname            109
 #define SYS_iopl                110
 #define SYS_vhangup             111
 #define SYS_idle                112
 #define SYS_vm86old             113
 #define SYS_wait4               114
 #define SYS_swapoff             115
 #define SYS_sysinfo             116
 #define SYS_ipc                 117
 #define SYS_fsync               118
 #define SYS_sigreturn           119
 #define SYS_clone               120
 #define SYS_setdomainname       121
 #define SYS_uname               122
 #define SYS_modify_ldt          123
 #define SYS_adjtimex            124
 #define SYS_mprotect            125
 #define SYS_sigprocmask         126
 #define SYS_create_module       127
 #define SYS_init_module         128
 #define SYS_delete_module       129
 #define SYS_get_kernel_syms     130
 #define SYS_quotactl            131
 #define SYS_getpgid             132
 #define SYS_fchdir              133
 #define SYS_bdflush             134
 #define SYS_sysfs               135
 #define SYS_personality         136
 #define SYS_afs_syscall         137 /* Syscall за файлова система Andrew */
 #define SYS_setfsuid            138
 #define SYS_setfsgid            139
 #define SYS__llseek             140
 #define SYS_getdents            141
 #define SYS__newselect          142
 #define SYS_flock               143
 #define SYS_syscall_flock       SYS_flock
 #define SYS_msync               144
 #define SYS_readv               145
 #define SYS_syscall_readv       SYS_readv
 #define SYS_writev              146
 #define SYS_syscall_writev      SYS_writev
 #define SYS_getsid              147
 #define SYS_fdatasync           148
 #define SYS__sysctl             149
 #define SYS_mlock               150
 #define SYS_munlock             151
 #define SYS_mlockall            152
 #define SYS_munlockall          153
 #define SYS_sched_setparam      154
 #define SYS_sched_getparam      155
 #define SYS_sched_setscheduler  156
 #define SYS_sched_getscheduler  157
 #define SYS_sched_yield         158
 #define SYS_sched_get_priority_max      159
 #define SYS_sched_get_priority_min      160
 #define SYS_sched_rr_get_interval       161
 #define SYS_nanosleep           162
 #define SYS_mremap              163
 #define SYS_setresuid           164
 #define SYS_getresuid           165
 #define SYS_vm86                166
 #define SYS_query_module        167
 #define SYS_poll                168
 #define SYS_syscall_poll        SYS_poll
 
 #endif  /* <sys/syscall.h> */
 
 
 Всяко системно повикване има определен номер (погледни листинга по-горе), който реално се
 използва за системно повикване.
 
 Ядрото използва прекъсване 0x80 за обслужване на всяко системно повикване. Номерът на
 systemcall-а и всички необходими аргументи се преместват в някои регистри (eax за номер на systemcall,
 напр,).
 
 номера на системното повикване представлява номер в масив на структура на ядрото, наречен
 sys_call_table[]. Тази структура съпоставя номерата на системните повиквания  и необходимите
 сервизни функции.
 
 Е, това знание трябва да е достатъчно, за да продължим с четенето. В следващата таблица е
 даден списък на най-интересните повиквания заедно с кратко описание.
 Повярвайте ми, трябва да знаете точното действие на тези повиквания, за да 
 направите наистина полезни LKMи.
 
 
 
системно повикване описание
int sys_brk(unsigned long new_brk); променя размера на използвания DS (сегмент за данни) ->това повикване ще бъде дискутирано в I.4
int sys_fork(struct pt_regs regs); systemcall отговарящ на добре познатата в потребителското пространство функция fork() 
int sys_getuid ()
int sys_setuid (uid_t uid)
...
повиквания за управление на UID и под.
int sys_get_kernel_sysms(struct kernel_sym *table) systemcall за достъп до системната таблица на ядрото (kernel system table) (-> I.3)
int sys_sethostname (char *name, int len);
int sys_gethostname (char *name, int len);
sys_sethostname отговаря за установяването на hostname, а sys_gethostname - за получаването му(?)
int sys_chdir (const char *path);
int sys_fchdir (unsigned int fd);
и двете функции се използват за установяване на текущата директория (cd ...)
int sys_chmod (const char *filename, mode_t mode);
int sys_chown (const char *filename, mode_t mode);
int sys_fchmod (unsigned int fildes, mode_t mode);
int sys_fchown (unsigned int fildes, mode_t mode);
функции за управление на правата и под.
int sys_chroot (const char *filename); настройва основната (root) директория за извикващия процес
int sys_execve (struct pt_regs regs); Много важен systemcall -> отговаря за изпълнението на файлове (pt_regs е регистров стек)
long sys_fcntl (unsigned int fd, unsigned int cmd, unsigned long arg); променя характеристиките на fd (opened file descr.)
int sys_link (const char *oldname, const char *newname);
int sym_link (const char *oldname, const char *newname);
int sys_unlink (const char *name);
системно повикване за управление на твърди (hardlinks) или "меки" (softlinks) връзки
int sys_rename (const char *oldname, const char *newname); преименува файлове
int sys_rmdir (const char* name);
int sys_mkdir (const *char filename, int mode);
създава и премахва директории
int sys_open (const char *filename, int mode);
int sys_close (unsigned int fd);
за всичко свързано с отварянето на файлове (включително създаването им), а също и затварянето им
int sys_read (unsigned int fd, char *buf, unsigned int count);
int sys_write (unsigned int fd, char *buf, unsigned int count);
системни повиквания за запис и четене от файлове
int sys_getdents (unsigned int fd, struct dirent *dirent, unsigned int count); systemcall, чрез който се извлича списък на файлове (командата ls ...) 
int sys_readlink (const char *path, char *buf, int bufsize); чете символични връзки
int sys_selectt (int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp); multiplexing на I/O операциите
sys_socketcall (int call, unsigned long args); socket функции
unsigned long sys_create_module (char *name, unsigned long size);
int sys_delete_module (char *name);
int sys_query_module (const char *name, int which, void *buf, size_t bufsize, size_t *ret);
използва се за зареждане / отстраняване на LKMи и за запитвания
По мое мнение, това са най-интересните системни повиквания за имащите хакерски намерения, разбира се, възможно е да се нуждаете от нещо специално на системата в която тършувате, но обикновенният хакер има изобилие от възможности със списъка даден по-горе. В част II ще се научите как да използвате системните повиквания в своя полза.

3. Какво представлява Kernel-Symbol-Table (таблица със символите на ядрото)

символ - нещо (напр. графично изображение), което представлява/олицетворява обект/събект/нещо абстрактно т.е. всеки символ на ядрото представлява някаква негова функция
бел. пр.
Е, добре! Разбираме основната концепция за системните повиквания (systemcalls) и модулите. Но има още нещо, и то много важно, което трябва да разберем - Kernel Symbol Table (таблица със символите на ядрото. Погледнете /proc/ksyms. Всеки ред в този файл представлява експортиран (публичен) символ на ядрото, който е достъпен за нашият LKM. Огледайте добре този файл; ще откриете много интересни неща в него.
Файлът е наистина интересен и може да ни помогне да разберем какво може да използва нашият LKM , но има един проблем. Всеки символ използван в нашият LKM (като функция) също се експортира в този "публичен регистър" т.е. появява се в този файл-списък. Така един опитен администратор би могъл да открие малкият ни LKM и да го отстрани (kill).
Съществуват много методи, чрез които да не позволим на админите да видят нашият LKM, вижте раздел II.
Матодите споменати в II могат да бъдат наречени 'хакове', но когато разгледате съдържанието на раздел II няма да откриете препратка към "Как да държиме LKM символите далеч от /proc/ksyms". Причината да не споменаваме този проблем в раздел II е следната :
няма да се нуждаете от трик, за да не допуснете модули-символи си до /proc/ksyms. Разработчиците на LKM могат да използват следният отрязък "нормален" код, за да спрат експортираните символи от техният модул:
static struct symbol_table module_syms= { /*дефинираме собствена таблица на символите !*/
  #include <linux/symtab_begin.h>          /*Символите които искаме да експортираме, а искаме ли ?*/
    ...                                       
 };
 
 register_symtab(&module_syms);            /*извършва истинското регистриране*/
 
 
 Тъй като не искаме да изнасяме никакви символи на показ, използваме
 следната конструкция :
 
 
register_symtab(NULL);


Този ред трябва да бъде вмъкнат във функцията init_module(), запомнете го !



<< (почти)Пълно ръководство за модулите в Linux ядрото [част 2] | Как да защитите вашият Linux (част 2) >>

Авторите на сайта, както и техните сътрудници запазват авторските права върху собствените си материали публикувани тук, но те са copyleft т.е. могат свободно да бъдат копирани и разпространявани с изискването изрично да се упоменава името на автора, както и да се публикува на видно място, че те са взети от оригиналния им URL-адрес на този сървър (http://www.linux-bg.org). Авторските права на преводните материали принадлежат на техните автори. Ако с публикуването тук на някакъв материал неволно са нарушени нечии права - след констатирането на този факт материалът ще бъде свален.

All trademarks, logos and copyrights mentioned on this site are the property of their respective owners.
Linux is copyright by Linus Torvalds.
© Линукс за българи ЕООД 2007
© Slavei Karadjov 1999 - 2006

All rights reserved.

Изпълнението отне: 0 wallclock secs ( 0.16 usr + 0.04 sys = 0.20 CPU)