Table of Contents

Tema 1 - Interceptare apeluri de sistem

Termen de predare: Joi, 21 Martie 2013, ora 23:00

Scopul temei

Monitorizarea apelurilor de sistem venite din partea proceselor, prin crearea unui interceptor de apeluri de sistem în kernel.

Enunț

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:

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:

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.

Precizări Linux

Exportarea tabelei de apeluri de sistem

Dacă aveți ultima mașină virtuală de pe site (kernel 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.

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;

Precizări Windows

Schiță pentru interceptarea apelurilor de sistem în Windows

NTSTATUS (*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;
}

Testare

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.

Linux

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.

Windows

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ări

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.

Întrebări

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).

FAQ

Q: Ce ar trebui să se întample în următoarele situații:

  1. s-a cerut monitorizarea tuturor proceselor, și apoi se cere demonitorizarea unui proces specific.
  2. s-a cerut monitorizarea tuturor proceselor, și apoi se cere monitorizarea unui proces specific.
  3. s-a cerut monitorizarea unui proces specific, și apoi se cere demonitorizarea tuturor.

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].