Differences

This shows you the differences between two versions of the page.

Link to this comparison view

so2:laboratoare:lab02 [2019/02/25 15:08]
ionel.ghita [Cuvinte cheie]
so2:laboratoare:lab02 [2019/02/25 15:09] (current)
ionel.ghita [Tracing]
Line 638: Line 638:
 # revenire din KDB # revenire din KDB
 kdb> go kdb> go
-</​code>​ 
- 
-==== Tracing ==== 
- 
-=== Kprobes === 
- 
-''​Kprobes''​ permite instrumentarea dinamică a oricărei funcții din kernel pentru a colecta informații de debug într-un mod cât mai puțin invaziv. Cu kprobe, putem seta breakpoints la aproape orice adresă din kernel și configura handlere ce vor fi invocate în aceste puncte. Pot fi folosite trei tipuri de probe: ''​Kprobes'',​ ''​Jprobes''​ și ''​Kretprobes''​ (sau probe de return). 
-  - O ''​Kprobe''​ poate fi inserată la orice adresă (instrucțiune) din kernel și putem specifica handlere ce vor fi apelate înainte sau după execuția instrucțiunii. 
-  - ''​Jprobe''​ va fi adăugată la entry-pointul unei funcții, utilă pentru a monitoriza argumentele funcțiilor. 
-  - O probă de return (''​Kretprobe''​) va fi apelată la întoarcerea dintr-o funcție, unde putem analiza rezultatul întors de aceasta. 
- 
-De cele mai multe ori se va realiza instrumentarea cu kprobe prin intermediul unui modul. Funcția module_init va instala una sau mai multe probe care vor fi dezactivate apoi în module_exit. Înregistrarea unei probe (e.x. ''​register_kprobe()''​) presupune specificarea adresei la care aceasta va fi inserată și a unui handler ce va fi apelat când se ajunge la adresa setată. Pentru mai multe detalii despre modul de funcționare ''​Kprobe'',​ ''​Jprobe''​ sau ''​Kretprobe'',​ expandați secțiunea de mai jos. 
- 
-<spoiler onHidden="​Afișați detalii implementare Kprobe"​ onVisible="​Ascundeți detalii implementare Kprobe"​ > 
- 
-**Kprobe** 
- 
-  - La înregistrarea unei probe, ''​Kprobes''​ face o copie a instrucțiunii de la adresa specificată (instrucțiunea pe care dorim să o instrumentăm) și înlocuiește primii bytes cu o instrucțiune de breakpoint (e.g. int3). 
-  - Când procesorul ajunge la instrucțiunea de breakpoint, se execută un trap și controlul revine la ''​Kprobes''​ (printr-un mecanism de tipul [[http://​lwn.net/​Articles/​160953/​ | notifier_call_chain]]). În acest punct, ''​Kprobes''​ va executa pre_handler-ul asociat probei. 
-  - După pre-handler,​ Kprobes execută instrucțiunea instrumentată,​ urmată post_handler-ul asociat. 
-  - Execuția continuă cu următoarea instrucțiune după cea instrumentată. 
- 
-**Jprobe** 
- 
-JProbe este implementat prin inserarea unei kprobe la entry-pointul funcției. Folosește un mecanism de mirroring pentru a permite accesul la argumentele funcției instrumentate. Se așteaptă ca handler-ul înregistrat:​ 
-  * să aibă aceeași semnătură (lista de argumente și return type) ca funcția instrumentată. 
-  * să se termine mereu cu apelarea funcției ''​jprobe_return()''​. 
- 
-Când se ajunge la un breakpoint, ''​Kprobes''​ face o copie a registrelor și a stivei, setând apoi instruction pointerul către handlerul înregistrat. Astfel, handlerul va fi apelat în aceeași stare (registre și stivă) ca funcția instrumentată. La ieșirea din handler, ''​jprobe_return()''​ restaurează conținutul inițial al stivei și al registrelor și continuă execuția cu funcția instrumentată. 
- 
-**Kretprobe** 
- 
-La înregistrarea unei probe de return, ''​Kprobes''​ instalează un kprobe la intrarea în funcția instrumentată. Acest kprobe are rolul de a salva o copie a adresei de return și de a înlocui adresa de return cu adresa unei porțiuni aleatoare de cod - o "​trambulină"​ (e.x. o instrucțiune nop). Astfel, funcția instrumentată se va întoarce în codul din "​trambulină"​. 
- 
-La inițializare,​ ''​Kprobes''​ setează un breakpoint la adresa acestei "​trambuline"​. La acest breakpoint se va apela handlerul asociat kretprobe, si se va reseta IP-ul la adresa de return salvată anterior (adresa de return inițială a funcției instrumentate). 
- 
-{{:​so2:​wiki:​kretprobe.png?​700X297}} 
- 
-</​spoiler>​ 
- 
-\\ 
- 
-** Înregistrare Kprobe ** 
- 
-Pentru înregistrarea unei ''​Kprobe''​ se va folosi o structură de tipul [[http://​lxr.free-electrons.com/​source/​include/​linux/​kprobes.h#​L73 | struct kprobe]]. Aici putem seta ''​addr''​ - adresa la care se va insera proba, ''​pre_handler''/''​post_handler'',​ metode ce vor fi apelate înainte/​după executarea instrucțiunii de la ''​addr''​ și ''​fault_handler''​ ce va fi apelat dacă apare un fault în handlerele pre/post sau la execuția instrucțiunii instrumentate. Adresa la care vrem să inserăm proba se poate specifica fie direct, fie folosind funcția [[http://​lxr.free-electrons.com/​source/​kernel/​kallsyms.c?​v=2.6.33#​L170 | kallsyms_lookup_name]] ce returnează adresa unui simbol primit ca parameru. 
- 
-<code c> 
-struct kprobe { 
-     /* location of the probe point */ 
-     ​kprobe_opcode_t *addr; 
-     /* Called before addr is executed. */ 
-     ​kprobe_pre_handler_t pre_handler;​ 
-     /* Called after addr is executed, unless... */ 
-     ​kprobe_post_handler_t post_handler;​ 
-     /* Called if executing addr or handlers causes a fault (eg. page fault). */ 
-     ​kprobe_fault_handler_t fault_handler;​ 
-     ... 
-} 
-</​code>​ 
- 
-Pentru înregistrarea/​deînregistrarea unei probe se vor apela funcțiile [[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1472 | register_kprobe]],​ respectiv [[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1577 | unregister_kprobe]]. Mai jos puteți urmări un exemplu în care este inițializată o probă de tipul ''​Kprobe''​. 
- 
-<code c> 
-static struct kprobe kp; 
-  
-/* kprobe pre_handler:​ called just before the probed instruction is executed */ 
-int handler_pre(struct kprobe *p, struct pt_regs *regs); 
-  
-/* kprobe post_handler:​ called after the probed instruction is executed */ 
-void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags); 
-  
-/* fault_handler:​ this is called if an exception is generated for any 
- * instruction within the pre- or post-handler,​ or when Kprobes 
- * single-steps the probed instruction. 
- */ 
-int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr); 
-  
-int init_module(void) 
-{ 
-         ​kp.pre_handler = handler_pre;​ 
-         ​kp.post_handler = handler_post;​ 
-         ​kp.fault_handler = handler_fault;​ 
-         ​kp.addr = (kprobe_opcode_t*) probe_addr; 
-  
-         if ((ret = register_kprobe(&​kp) < 0)) { 
-                 ​printk("​register_kprobe failed, returned %d\n", ret); 
-                 ​return -1; 
-         } 
-         ​printk("​kprobe registered\n"​);​ 
-         ​return 0; 
-} 
- 
-</​code>​ 
- 
-** Înregistrare Jprobe ** 
- 
-Pentru ''​Jprobe'',​ vom folosi o structură de tipul [[http://​lxr.free-electrons.com/​source/​include/​linux/​kprobes.h?​v=2.6.39#​L158 | struct jprobe]], al cărei câmp ''​entry''​ determină adresa handlerului ce va fi apelat la intrarea în funcția instrumentată. Pentru înregistrare/​deînregistrare se vor folosi [[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1703 | register_jprobe]]/​[[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1740 |unregister_jprobe]]. În exemplul de mai jos se folosește un modul pentru a monitoriza apelurile funcției ''​do_execveat_common''​. 
- 
-<note important>​ 
-  * Semnătura handlerului trebuie să fie aceeași cu semnătura funcției instrumentate. 
-  * Nu uitați să apelați jprobe_return() la ieșirea din handler. 
-</​note>​ 
- 
-<file C kprobes.c>​ 
-#include <​linux/​module.h>​ 
-#include <​linux/​init.h>​ 
-#include <​linux/​kernel.h>​ 
-#include <​linux/​kprobes.h>​ 
-#include <​linux/​kallsyms.h>​ 
- 
-MODULE_DESCRIPTION("​Probes module"​);​ 
-MODULE_AUTHOR("​So2rul Esforever"​);​ 
-MODULE_LICENSE("​GPL"​);​ 
- 
-/* 
- * Pre-entry point for do_execve. 
- */ 
-static int my_do_execveat_common(int fd, struct filename * filename, 
- char __user *__user *argv, 
- char __user *__user *envp) 
-{ 
- pr_info("​%s(%s) [%s] \n", __func__, filename->​name,​ current->​comm);​ 
- /* Always end with a call to jprobe_return(). */ 
- jprobe_return();​ 
- /​*NOTREACHED*/​ 
- return 0; 
-} 
- 
-static struct jprobe my_jprobe = { 
- .entry = (kprobe_opcode_t *) my_do_execveat_common 
-}; 
- 
-static int my_probe_init(void) 
-{ 
- int ret; 
- 
- my_jprobe.kp.addr = 
- (kprobe_opcode_t *) kallsyms_lookup_name("​do_execveat_common"​);​ 
- if (my_jprobe.kp.addr == NULL) { 
- pr_err("​Couldn'​t find %s to plant jprobe\n",​ "​do_execveat_common"​);​ 
- return -1; 
- } 
- 
- ret = register_jprobe(&​my_jprobe);​ 
- if (ret < 0) { 
- pr_err("​register_jprobe failed, returned %d\n", ret); 
- return -1; 
- } 
- pr_info("​Planted jprobe at %p, handler addr %p\n", 
- my_jprobe.kp.addr,​ my_jprobe.entry);​ 
- 
- return 0; 
-} 
- 
-static void my_probe_exit(void) 
-{ 
- unregister_jprobe(&​my_jprobe);​ 
- pr_info("​jprobe unregistered\n"​);​ 
-} 
- 
-module_init(my_probe_init);​ 
-module_exit(my_probe_exit);​ 
-</​file>​ 
- 
-** Înregistrare Kretprobe ** 
- 
-''​Kretprobes''​ se vor insera într-un mod similar, folosind o structură de tipul [[http://​lxr.free-electrons.com/​source/​include/​linux/​kprobes.h#​L184 | struct kretprobe]] și funcțiile [[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1811 | register_kretprobe]],​ [[http://​lxr.free-electrons.com/​source/​kernel/​kprobes.c#​L1882 | unregister_kretprobe]]. 
- 
-<code c> 
- 
-static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs); 
-  
-static struct kretprobe my_kretprobe = { 
-         ​.handler = ret_handler,​ 
-}; 
- 
 </​code>​ </​code>​
  
so2/laboratoare/lab02.txt · Last modified: 2019/02/25 15:09 by ionel.ghita
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0