This shows you the differences between two versions of the page.
|
so2:laboratoare:lab05:exercitii [2018/03/21 09:51] ionel.ghita [4. [2p] Citirea bufferului printr-un apel ''read''] |
so2:laboratoare:lab05:exercitii [2019/03/20 11:49] (current) gabriel.bercaru [1. [2.5p] Alocare de porturi I/O] - fixed typo ('kdb' instead of 'kbd') |
||
|---|---|---|---|
| Line 104: | Line 104: | ||
| Obiectivul exercițiilor următoare este să creați un driver pentru a intercepta tastele apăsate (//keylogger//). Driver-ul va intercepta IRQ-ul destinat controller-ului de tastatură și va inspecta codurile tastelor primite, stocându-le într-un buffer. | Obiectivul exercițiilor următoare este să creați un driver pentru a intercepta tastele apăsate (//keylogger//). Driver-ul va intercepta IRQ-ul destinat controller-ului de tastatură și va inspecta codurile tastelor primite, stocându-le într-un buffer. | ||
| - | ==== 1. [1.5p] Alocare de porturi I/O ==== | + | ==== 1. [2.5p] Alocare de porturi I/O ==== |
| Pentru început ne propunem să alocăm spații din zona I/O pentru dispozitive hardware. Vom vedea că nu putem să alocăm spațiu pentru tastatură întrucât regiunea aferentă e deja alocată. Apoi vom aloca spațiu I/O pentru porturi care nu sunt folosite. | Pentru început ne propunem să alocăm spații din zona I/O pentru dispozitive hardware. Vom vedea că nu putem să alocăm spațiu pentru tastatură întrucât regiunea aferentă e deja alocată. Apoi vom aloca spațiu I/O pentru porturi care nu sunt folosite. | ||
| Line 141: | Line 141: | ||
| Descărcați modulul din kernel folosind comanda<code> | Descărcați modulul din kernel folosind comanda<code> | ||
| - | rmmod kdb | + | rmmod kbd |
| </code> și verificați acum conținutul fișierului ''/proc/ioports''; observți că a dispărut alocarea spațiului I/O pentru cele două porturi. | </code> și verificați acum conținutul fișierului ''/proc/ioports''; observți că a dispărut alocarea spațiului I/O pentru cele două porturi. | ||
| Line 148: | Line 148: | ||
| </note> | </note> | ||
| - | ==== 2. [1.5p] Rutina de tratare a întreruperii ==== | + | ==== 2. [2p] Rutina de tratare a întreruperii ==== |
| Urmărim să înregistrăm o rutină de tratare a întreruperii de tastatură. Veți scrie o funcție (rutină) de tratare a înteruperii și o veți înregistra folosind ''[[https://elixir.bootlin.com/linux/v4.15/source/include/linux/interrupt.h#L145|request_irq]]''. | Urmărim să înregistrăm o rutină de tratare a întreruperii de tastatură. Veți scrie o funcție (rutină) de tratare a înteruperii și o veți înregistra folosind ''[[https://elixir.bootlin.com/linux/v4.15/source/include/linux/interrupt.h#L145|request_irq]]''. | ||
| Line 207: | Line 207: | ||
| <note tip> | <note tip> | ||
| Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<code> | Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<code> | ||
| - | pr_info("IRQ:% d, scancode = 0x% x (% u,% c) \ n" | + | pr_info("IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n", |
| - | irq_no, scancode, scancode, scancode); | + | irq_no, scancode, scancode, pressed, ch); |
| </code> | </code> | ||
| unde ''scancode'' este valoarea registrului citit cu ajutorul funcției ''i8042_read_data()''. | unde ''scancode'' este valoarea registrului citit cu ajutorul funcției ''i8042_read_data()''. | ||
| Line 256: | Line 256: | ||
| <note tip> | <note tip> | ||
| Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<code> | Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<code> | ||
| - | printk(LOG_LEVEL "IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n", | + | pr_info(LOG_LEVEL "IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n", |
| irq_no, scancode, scancode, pressed, ch); | irq_no, scancode, scancode, pressed, ch); | ||
| </code> | </code> | ||
| Line 299: | Line 299: | ||
| </note> | </note> | ||
| - | Completați funcția ''get_char'' pentru a obține următorul caracter care trebuie citit din buffer. Aveți grijă la implementarea buffer-ului circular (să nu depașiți citiți dincolo de dimensiunea buffer-ului și de poziția de scriere). | + | Completați funcția ''get_char'' pentru a obține următorul caracter care trebuie citit din buffer. Aveți grijă la implementarea buffer-ului circular (să nu citiți dincolo de dimensiunea buffer-ului și de poziția de scriere). |
| Pentru copierea în user space veți folosi apelul [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/include/asm/uaccess.h#L249|put_user]] pentru fiecare caracter în parte, obținut folosind funcția ''get_char''. | Pentru copierea în user space veți folosi apelul [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/include/asm/uaccess.h#L249|put_user]] pentru fiecare caracter în parte, obținut folosind funcția ''get_char''. | ||
| Line 324: | Line 324: | ||
| O să obțineți conținutul bufferului keylogger-ului din kernel space. | O să obțineți conținutul bufferului keylogger-ului din kernel space. | ||
| - | ==== 5. [3p] Curățarea buffer-ului dacă este tastată o parolă ==== | + | ==== 5. [1.5p] Curățarea buffer-ului la scriere ==== |
| - | Dorim să %%"curățăm"%% buffer-ul dacă este tastată o parolă. În acel moment buffer-ul va fi resetat: conținutul său va fi resetat și dimensiunea va fi ''0''. | + | Dorim să %%"curățăm"%% buffer-ul atunci când se scrie în device node-ul asociat driver-ului. În acel moment buffer-ul va fi resetat: conținutul său va fi resetat și dimensiunea va fi ''0''. |
| - | Implementarea o veți face în fișierul ''kbd.c''. În rutina de tratare a întreruperii veți reține în câmpul ''passcnt'' câte caractere din parolă au fost potrivite. Parola este definită de macro-urile ''MAGIC_WORD'' și ''MAGIC_WORD_LEN''. | + | Implementarea o veți face în fișierul ''kbd.c''. Urmăriți comentariile marcate cu ''TODO 5''. Implementați funcția ''reset_buffer'' și adăugați operația ''write'' la ''kbd_fops''. |
| - | + | ||
| - | Când se detectează întreaga parolă, curătați buffer-ul. Pentru curățare, reinițializați câmpul ''buf_idx'' la valoarea ''0'' și umpleți cu ''0''-uri buffer-ul. | + | |
| - | + | ||
| - | La primirea unui caracter nou, verificați dacă acesta corespunde cu caracterul de pe poziția curentă din parolă. În caz afirmativ, incrementați contorul; altfel, resetați counterul la 0. | + | |
| <note tip> | <note tip> | ||
| - | În momentul resetării buffer-ului, în handler-ul de întrerupere va trebui să folosiți spinlock-ul pentru accesul exclusiv la buffer. Este posibil ca rutina de read să fie procesată de pe alt procesor și să acceseze buffer-ul în momentul în care handler-ul de întrerupere îl modifică. Veți folosi funcțiile [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/spinlock.h#L308|spin_lock]] și [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/spinlock.h#L348|spin_unlock]]. | + | În momentul resetării buffer-ului, va trebui să folosiți spinlock-ul cu dezactivarea întreruperilor pentru accesul exclusiv la buffer. |
| Parcurgeți secțiunea [[:so2:laboratoare:lab05#utilizare_spinlock-uri|Utilizarea spinlock-uri]]. | Parcurgeți secțiunea [[:so2:laboratoare:lab05#utilizare_spinlock-uri|Utilizarea spinlock-uri]]. | ||
| Line 347: | Line 343: | ||
| cat /dev/kbd | cat /dev/kbd | ||
| </code> | </code> | ||
| - | Apăsați pe taste și apoi apăsați conținutul macro-ului ''MAGIC_WORD'' (adică ''root'') și apoi consultați din nou conținutul buffer-ului. Ar trebui să fie resetat. | + | Apăsați pe taste și apoi rulați comanda ''echo "clear" > /dev/kbd'' și apoi consultați din nou conținutul buffer-ului. Ar trebui să fie resetat. |
| ===== Extra ===== | ===== Extra ===== | ||
| - | |||
| - | ==== [1 karma] Resetarea buffer-ului în rutina de read ==== | ||
| - | |||
| - | Implementarea curentă este problematică pentru că rutina de tratare a întreruperii este cea care face resetarea buffer-ului, lucru care este consumator de timp și nerecomandat să fie făcut în context întrerupere. | ||
| - | |||
| - | O soluție este să actualizăm comportamentul astfel încât resetarea buffer-ului să se producă la apelul funcției ''kbd_read()''. După ce informația este copiată în user space, buffer-ul este resetat. Nu mai resetăm în momentul tastării | ||
| - | |||
| - | Simplificați implementarea în așa fel încât resetarea buffer-ului să se producă în funcția ''kbd_read()'' imediat după copierea datele în user space. | ||
| - | |||
| - | <note tip> | ||
| - | În momentul în care copiați datele în buffer-ul temporare ''tmp_buf'' resetați buffer-ul principal: adică umpleți buffer-ul ''buf'' cu zero-uri și inițialiați ''buf_idx'' la ''0''. | ||
| - | </note> | ||
| - | |||
| - | Pentru verificarea după citirea din ''/dev/kbd'' veți avea buffer-ul resetat. Citiri ulterioare vor găsi buffer-ul gol. | ||
| ==== [3 karma] Utilizare kfifo ==== | ==== [3 karma] Utilizare kfifo ==== | ||