Termen de predare: Joi, 21 Martie 2013, ora 23:00
Monitorizarea apelurilor de sistem venite din partea proceselor, prin crearea unui interceptor de apeluri de sistem în kernel.
Să se scrie un modul de kernel sci care să intercepteze, la cerere, apeluri de sistem. Pentru apelurile de sistem interceptate, modulul trebuie să poată monitoriza anumite procese ce fac apeluri și să le jurnalizeze în syslog.
La inserare, modulul va rezerva un apel de sistem nefolosit (MY_SYSCALL_NO
) și va aștepta următoarele instrucțiuni (comenzi) din userspace prin respectivul apel:
REQUEST_SYSCALL_INTERCEPT
pentru a intercepta un apel de sistem REQUEST_SYSCALL_RELEASE
pentru a renunța la interceptarea unui apel de sistem REQUEST_START_MONITOR
pentru a porni monitorizarea unui apel de sistem (ce a fost în prealabil interceptat) pentru un anumit proces REQUEST_STOP_MONITOR
pentru a opri monitorizarea unui apel de sistem (ce a fost în prealabil interceptat) pentru un anumit proces Rutina de tratare a apelului rezervat va avea urmatoarea semnătură:
long my_syscall(int cmd, int syscall, int pid);
unde:
Interceptarea unui apel de sistem se referă la faptul că modulul va înlocui intrarea din tabela de apeluri de sistem, astfel încât toate apelurile ulterioare să “trecă prin modul” (și să ajungă în cele din urmă la apelul de sistem original).
Monitorizarea se refera la faptul că modulul va loga în userspace informații despre proces și apel de sistem: numărul apelului de sistem, parametrii apelului de sistem, codul de eroare întors de apelul de sistem (cel original), PID-ul procesului.
Monitorizarea se poate face pentru un proces sau pentru toate procesele (caz în care pid este 0).
Înainte de a efectua operațiile cerute din userspace, modulul va trebui să facă verificări de consistență, drepturi, stare și să semnaleze, daca este cazul, condițiile de eroare:
MY_SYSCALL_NO
și __NR_exit_group
)Se impune folosirea unei liste pentru menținerea informațiilor despre procesele monitorizate. Folosirea unui vector de dimensiune fixă se consideră neadecvată pentru că timpul de căutare va fi similar, complexitatea implementării va fi similară, vectorul va avea un număr limitat de intrări. Se recomandă folosirea structurilor existente în kernel.
Pentru că numărul de apeluri de sistem este în general mic (250-300 apeluri de sistem), dar și pentru o latență cât mai mica introdusă de interceptare, se recomandă ca informațiile despre apelurile de sistem interceptate să se țină într-un vector.
Pentru interceptarea de apeluri de sistem revedeți informațiile prezentate în Cursul 2.
3.7.8-s02 #3
), modificările sunt deja făcute. Recomandăm ca, în caz contrar, să luați mașina de pe pagina de mașini virtuale.
arch/x86/kernel/i386_ksyms_32.c
următoarele linii:extern void* sys_call_table[]; EXPORT_SYMBOL(sys_call_table);
De asemenea, va trebuie să modificați arch/x86/kernel/syscall_32.c
pentru a face tabela writable:
- const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { + sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
În plus, pentru că macro-ul NR_syscalls
nu mai este definit în nucleu, va trebui să definiți o variabilă specială care să conțină numărul de apeluri de sistem în arch/x86/kernel/entry_32.S
. Nucleul mașinii virtuale definește variabila my_nr_syscalls
în acest sens.
ENTRY(my_nr_syscalls) .long nr_syscalls
De asemenea, trebuie exportată variabila de mai sus în arch/x86/kernel/i386_ksyms_32.c
:
extern long my_nr_syscalls; EXPORT_SYMBOL(my_nr_syscalls);
Imaginea de pe site are făcute aceste modificări;
sys_call_table
și my_nr_syscalls
) va trebui să le marcați cu extern:extern void *sys_call_table[]; extern long my_nr_syscalls;
my_syscall
, care în prezent nu mai este folositroot
(uid 0)struct task_struct
) task_struct
-ul procesului curent este dat de macroul currenttask_struct
-ul unui proces oarecare folosiți funcțiile pid_task() și find_vpid()sci_lin.h
(fișierul se găsește în arhiva de teste)-EINVAL
pentru parametru invalid-EBUSY
pentru ocupat-EPERM
pentru access refuzat-ENOMEM
pentru memorie insuficientă0
pentru successci_win.h
(fișierul se găsește în arhiva de teste) să nu funcționeze pe alte versiunistruct std KeServiceDescriptorTable[2]; struct std *KeServiceDescriptorTableShadow;
Structura std
este definită în sci_win.h
sci_win.h
la MY_SYSCALL_NO; puteți presupune că această valoare este întotdeauna mai mare decât numărul de apeluri de sistem prezentesci_win.h
; pentru a verifica dacă doi utilizatori sunt identici folosiți funcția CheckUserssci_win.h
struct packet_log
definite în sci_win.h
, pachete ce vor fi încapsulate în câmpul DumpData al unei intrări de log (vezi IoWriteErrorLogEntry) cu codul de eroare 0sci_win.h
) pentru a opri, respectiv porni protecția la scriereSTATUS_INVALID_PARAMETER
pentru parametru invalidSTATUS_DEVICE_BUSY
pentru ocupatSTATUS_ACCESS_DENIED
pentru access refuzatSTATUS_NO_MEMORY
pentru memorie insuficientăSTATUS_SUCCESS
pentru succesNTSTATUS (*f)(); NTSTATUS interceptor() { int syscall, param_size, syscall_table; int syscall_index, r; void *old_stack, *new_stack; _asm mov syscall, eax syscall_table = syscall >> 12; syscall_index = syscall & 0x0000FFF; param_size = KeServiceDescriptorTable[syscall_table].spt[syscall_index]; _asm mov old_stack, ebp _asm add old_stack, 8 _asm sub esp, param_size _asm mov new_stack, esp RtlCopyMemory(new_stack, old_stack, param_size); r = f(); DbgPrint("syscall %d returns %d\n", syscall, r); return r; } void intercept(int syscall) { int syscall_table, syscall_index; syscall_table = syscall >> 12; syscall_index = syscall & 0x0000FFF; f = KeServiceDescriptorTable[syscall_table].st[syscall_index]; /* save service descriptor table entry to f */ KeServiceDescriptorTable[syscall_table].st[syscall_index] = interceptor; KeServiceDescriptorTableShadow[syscall_table].st[syscall_index] = interceptor; }
Pentru simplificarea procesului de corectare al temelor, dar și pentru a reduce greșelile temelor trimise, corectarea temelor se va face automat cu ajutorul unor teste publice (Linux, Windows). Testele presupun că numele modului de kernel este denumit sci
atât pe Linux cât și pe Windows.
Testul se folosește de utilizatorul nobody
care ar trebui să existe pe toate sistemele Linux. Din această cauză testul trebuie să fie plasat intr-un director public.
Utilizatorul cu care veți rula testul trebuie să aibă drepturile “Act as part of the operating system”, “Create a token object” și “Replace a process level token”. Pentru a configura aceste drepturi: Start → Control Panel → Administrative Tools → Local Security Policy → Local Policies → User Rights Assignments.
Depunctările generale pentru teme se găsesc pe pagina de Indicații generale.
Depunctări pentru probleme constatate în implementarea temei prezente:
În cazuri excepționale (tema trece testele prin nerespectarea cerințelor) și în cazul în care tema nu trece toate testele se poate scădea mai mult decât este menționat mai sus.
Pentru întrebări legate de temă puteți consulta arhivele listei de discuții sau puteți trimite un e-mail (trebuie să fiți înregistrați).
Q: Ce ar trebui să se întample în următoarele situații:
A: Nu se vor testa asemenea condiții. Este recomandată o abordare simplă, în care pid=0 este considerat la fel ca PID -ul celorlalte procese. 1)
Q: La tema 1 pe windows, structura std este un pic ambiguă. Ce înseamna fiecare câmp ?
struct std { void **st; /* service table */ int *ct; /* counter table */ int ls; /* last service no */ unsigned char *spt; /* service parameter table */ };
A:
void **st; /* service table */
Un pointer la service call table, un vector de adrese ale handlerelor pentru fiecare system call, indexat dupa id-ul system call-ului.
int *ct; /* counter table */
Pentru profiling, folosit doar în debug builds. Un pointer la un vector de countere, indexat tot după id-ul system call-ului, care numară de câte ori a fost apelat fiecare system call.
int ls; /* last service no */
Numărul de intrări din service table / counter table / service parameter table.
unsigned char *spt; /* service parameter table */
Numărul de bytes ocupați de argumentele fiecarui system call.
Pentru dispatcher-ul vostru nu veți folosi decât service table și last service no, dar când creați noua tabelă este nevoie să duplicați și service parameter table.
Pentru mai multe informatii vezi [1], descrierea KeAddSystemServiceTable [2] si trap.asm, sursa dispatcher-ului de system calls pentru windows [3].