[ --- The Bug! Magazine _____ _ ___ _ /__ \ |__ ___ / __\_ _ __ _ / \ / /\/ '_ \ / _ \ /__\// | | |/ _` |/ / / / | | | | __/ / \/ \ |_| | (_| /\_/ \/ |_| |_|\___| \_____/\__,_|\__, \/ |___/ [ M . A . G . A . Z . I . N . E ] [ Numero 0x04 <---> Revisao 0x01 <---> Artigo 0x04 ] .> 23 de Junho de 2009, .> The Bug! Magazine < staff [at] thebugmagazine [dot] org > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- FreeBSD -- Basico Desenvolvimento de modulos +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .> 26 de Abril de 2009, .> Spike Spiegel < spikespiegel [at] gmail [dot] com > "Eterna mente, e ter na mente, eternamente, eter na mente" [ --- Indice + 1 <---> Introducao + 2 <---> FreeBSD + 2.1 <-> Secure levels + 2.2 <-> kldstat / kldload / kldunload + 2.3 <-> Debugging kernel + 3 <---> KLD + 3.1 <-> O que e um KLD ? + 3.2 <-> Inicializacao e remocao + 3.3 <-> Macro DECLARE_MODULE + 3.4 <-> Transferencia de dados entre Kernel Space e User Space + 3.4.1 <-> User Space --> Kernel Space + 3.4.2 <-> Kernel Space --> User Space + 3.5 <-> 'Hello World' module + 3.6 <-> Makefile + 4. <---> Syscall modules + 4.1 <-> Syscalls + 4.2 <-> 'Write' syscall + 4.3 <-> Interface + 5. <---> Hooking de syscall + 5.1 <-> 'ptrace' module + 6. <---> Finalizando... + 6.1 <-> Conclusoes + 6.2 <-> Referencias [ --- 1. Introducao Esse artigo e apenas uma introducao de como desenvolver modulos para o Fre- eBSD. Aqueles que ja tem alguma experiencia em desenvolvimento no kernel Linux nao encontrarao informacoes uteis nesse documento. Boas referencias podem ser encontradas na ultima parte desse documento. Sao necessarios conhecimentos basicos da linguagem C. Utilize alguma maquina virtual para realizar seus testes. Voce nao vai querer um kernel panic no meio de uma tarefa. E se voce ja tiver alguma experiencia e/ou contato com o FreeBSD, pule dire- tamente para a segunda parte e voce nao perdera seu tempo com futilidades. Todos os codigos nesse artigo foram testados no FreeBSD 7.1. Let's go! [ --- 2. FreeBSD [ --- 2.1. Secure levels Secure levels sao bem comuns no universo BSD. Esse recurso tem apenas o objeti- vo de limitar o acesso aos recursos do sistema, como o carregamento de modulos, escrita em /dev/kmem, nao permitir alteracao em arquivos importantes e etc. /etc/rc.conf -- E recomendado deixar a configuracao seu sistema de testes da seguinte forma. <---> kern_securelevel="-1" kern_securelevel_enable="YES" <---> Utilizando o valor -1, seu sistema estara mais suscetivel a entradas mais hard- cores. Mantenha esse valor quando for brincar com o Beastie. [ --- 2.2. kldstat e kld(un)load klstat Esse programa exibe todos os modulos carregados no sistema. Se voce executar o comando na shell, o output sera parecido com o abaixo. box72# kldstat Id Refs Address Size Name 1 3 0xc0400000 9fab28 kernel 2 1 0xc0dfb000 6a45c acpi.ko Mas esses modulos tambem tem submodulos. Para voce ter uma visao mais detalha- da das dependencias, utilize o comando kldstat -v kld(un)load Como o proprio nome ja diz, os comandos kldload e kldunload sao responsaveis por carregar e descarregar os modulos, respectivamente. [ --- 2.3. Debugging Kernel Realizar a tarefa de encontrar de problemas no seu modulo ou kernel e' uma arte negra. Conhecimento de assembly e arquitetura se fazem necessarios na hora de debugar um kernel. Existem 2 formas de debugar o kernel: ddb e kgdb. O ddb e' e um debugger precario, mas ao mesmo tempo inutil. Dependendo das configuracoes que voce utilizar em seu sistema, voce sempre vera' o ddb apos um kernel panic. Ao contrario do ddb, o kgdb e' um debugger mais poderoso, se trata de um gdb modificado especificamente para kernel debugging. Adicione as seguintes opcoes nas configuracoes do seu kernel. makeoptions="-g" options KDB options KDB_TRACE options DDB Se voce nao tem ideia de como criar um ambiente de testes e muito menos como utilizar o ddb/kgdb, entao recomendo a leitura do "Debugging Kernel Problems" [4]. Nele ha' a descricao de todas as ferramentas uteis que voce podera' utili- zar no caso de encontrar problemas e algumas praticas de debugging. O documento nao somente aborda FreeBSD, mas tambem NetBSD e OpenBSD. [ --- 3. KLD [ --- 3.1. O que sao KLDs ? KLD e' uma forma de voce adicionar mais recursos ao kernel. [ --- Todo KLD devera ter... [ --- 3.2. Uma funcao Inicializacao e Remocao Todos os modulos deverao ter uma funcao que realizara a inicializacao e remocao do modulo. Normalmente, se aproveita essa funcao para criar dispositivos, ini- cializar dados, fazer hooks e etc. Ja' na remocao se faz o processo inverso. static int load(struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: uprintf("Hello, world! (I'm syscall %d)\n", offset); break; case MOD_UNLOAD: uprintf("Good-bye, cruel world! (I'm no longer syscall %d)\n", offset); break; default: error = EOPNOTSUPP; break; } return(error); } Essa funcao imprime 'Hello, world! (I'm syscall offset)' (o valor de offset se- ra explicado mais a frente) quando o modulo for carregado e 'Good-bye, cruel world! (I'm no longer syscall offset) quando for removido. Em caso de outras operacoes, retorna' EOPNOTSUPP. [ --- 3.3. Macro DECLARE_MODULE Todos os modulos criados por voce deverao ter essa macro. E' essa macro que servira' como ponto inicial para o kernel, exatamente pelo conteudo adicionado nessa macro. ..[ ].. ._ nome do modulo | ._ moduledata | | ._ tipo do modulo | | | ._ momento que sera inicializado | | | | #define DECLARE_MODULE(name, data, sub, order) MODULE_METADATA(_md_##name, MDT_MODULE, &data, #name); SYSINIT(name##module, sub, order, module_register_init, &data) struct __hack [ --- 3.4. Transferencia de dados entre Kernel Space e User Space [ --- 3.4.1. User Space --> Kernel Space Existem 2 funcoes utilizadas para fazer transferencia de dados do UserSpace pa- ra o KernelSpace: copyint e copyinstr. Prototipos: int copyin(const void *uaddr, void *kaddr, size_t len); int copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done); A funcao copyin copia 'len' bytes de dados do uaddr (user address) para o kaddr(kernel address). A funcao copyinstr e' similar `a copyin, exceto que ela copia ate' o terminador nulo de uma string, copiando no maximo 'len' bytes. 'done' recebe a quantidade de dados que foi efetivamente copiada. [ --- 3.4.2. Kernel Space --> User Space Para fazer o processo inverso, ou seja, do KernelSpace para UserSpace, sao utilizadas as funcoes copyout e copystr. O uso delas e' similar ao das funcoes anteriores. int copyout(const void *kaddr, void *uaddr, size_t len); int copystr(const void *kfaddr, void *kdaddr, size_t len, size_t *done); Usaremos essas funcoes mais a frente. [ --- 3.5. 'Hello World' module Agora, juntando tudo, veremos uma classica mensagem. <---hello-world.c---> #include #include #include #include #include #include #include static int load(struct module *module, int cmd, void *arg) { int error = 0; switch(cmd) { case MOD_LOAD: uprintf("Hello, world!\n"); break; case MOD_UNLOAD: uprintf("Good-bye, cruel world!\n"); break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t hello_mod = { "hello", load, NULL }; DECLARE_MODULE(hello, hello_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); [ --- 3.6. Makefile Utilize o seguinte Makefile para compilar os seus modulos: <---Makefile---> SRCS=hello-world.c KMOD=hello-world .include Esse Makefile e' bastante generico e voce raramente precisara' mudar algo, pois os devs do freebsd criaram o 'bsd.kmod.mk' ja' contendo todas as instrucoes ne- cessarias para compilar o seu modulo. Para debugar seu modulo, adicione a seguinte flag no Makefile CFLAGS=-g Apos voce criar seu Makefile, execute o comando 'make' na shell e em seguida o seu modulo sera' compilado. Para testar o modulo, use o comando kldload. [-------- Output --------] box72# kldload ./hello-world.ko Hello, world! box72# kldunload ./hello-world.ko Good-bye, cruel world! [------- /Output --------] [ --- 4. Syscalls modules Syscall e' uma forma de voce se comunicar com o kernel. E' basicamente a inter- face entre o user space e kernel space. Um bom exemplo para isso e' o seguinte codigo assembly: movl $write, %eax ; syscall write movl $0x1, %ebx ; output movl $dead, %ecx ; texto movl $666, %edx ; tamanho do text int $0x80 O programa ira' carregar o registrador eax com o valor da nossa syscall, e em seguida, carregara ebx com o output, ecx com o texto e edx como tamanho do tex- to, depois gerara' a interrupcao 80h para o kernel, executando assim a syscall chamada pelo software e, em seguida, retornando o controle para o programa. v |-----------------| | | aplicacao | hello world-------------------+ | |-----------------| |---> User Space | | biblioteca libc | printf("hello world"); -------+ v |-----------------| | | system call | write(1, "hello world", 16); -+ | |-----------------| |---> Kernel Space | | kernel | ------------------------------+ v |-----------------| Essa nao e' a unica forma de se comunicar com o kernel. Existem varias possibi- lidades abertas que devem ser exploradas como criacao de dispositivos, ioctl e etc. [ --- 4.1. Syscalls Tres itens sao necessarios para a criacao de uma syscall: 1. A funcao: A syscall em si, que sera' responsavel pela tarefa. 2. A tabela sysent: Que contera' as informacoes sobre a syscall. 3. Offset: O numero identificador da syscall. [ --- 4.1.1. A funcao A seguinte funcao sera' a syscall. <++> /* * Argumentos */ struct argz { int value; }; /* * Syscall */ static int hello (struct thread *td, void *arg) { struct argz *uap = arg; printf ("value:\n" uap->value); return 0; } <++> A estrutura argz representa os argumentos que deverao ser repassados para a syscall, quando chamada. Na funcao hello, observe que existem 2 parametros. O primeiro e' uma referencia para a thread. Esse parametro e' utilizado pelo pro- cesso que esta chamando a syscall. [ --- 4.1.2. A tabela sysent Todas as syscalls no FreeBSD estao registradas nessa tabela. Se voce fizer uma pesquisa rapida nessa tabela, ira observar que todas as syscall susportadas pe- lo FreeBSD possuem uma entrada nessa tabela. <++> static struct sysent hello_sysent = { 1, /* Numero de argumentos */ hello /* Syscall */ }; <++> [ --- 4.1.3. Offset Offset e' o valor da syscall. Ele serve para identificar a syscall. Se voce de- sejar definir o proprio valor da syscall, escolha um valor que nao esteja sen- do utilizado por nenhuma outra syscall, ou entao atribua o valor NO_SYSCALL. <++> static int offset = NO_SYSCALL; <++> [ --- 4.2. 'Print' syscall (Write ?) Juntando tudo que voce viu na explicacoes anteriores, ja' e possivel criar uma nova syscall. <---print.c---> #include #include #include #include #include #include #include #include struct argz { char *buf; int len; }; static int print (struct thread *td, void *arg) { char buf[512]; struct argz *uap=arg; copyin(uap->buf, buf, uap->len); uprintf("%s\n", buf); return 0; } static struct sysent print_sysent = { 2, print }; static int offset = NO_SYSCALL; static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : printf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : printf ("syscall unloaded from %d\n", offset); break; default : error = EOPNOTSUPP; break; } return error; } SYSCALL_MODULE(print, &offset, &print_sysent, load, NULL); Se voce ja' tem nocoes de programacao segura, notou que a nossa syscall nao e segura. Entao meu amigo, os mesmos cuidados que voce tem ao desenvolver na Linguagem C em userland sao necessarios em kernelland. Voce nao vai querer que atacantes brinquem em seu sistema. [ --- 4.3. Interface Para testar a syscall, voce pode criar um codigo em assembly que ira executar a syscall. Isso e' bem trivial, se tiver algum conhecimento em assembly. Existe tambem em C uma funcao que voce podera' chamar para executar a syscall. Essa funcao se chama syscall(). "duhh" O seguinte codigo, retirado de um livro, podera' ser utilizado para a execucao da syscall. <---interface.c---> /** * Nome: interface.c */ #include #include #include #include int main(int argc, char **argv) { int syscall_num; struct module_stat stat; if(argc != 2) printf("usage: \n%s \n", argv[0]), exit(0); stat.version = sizeof(stat); modstat(modfind("print"), &stat); syscall_num = stat.data.intval; return(syscall(syscall_num, argv[1], strlen(argv[1]) + 1)); } Observe a existencia das funcoes modstat e modfind, utilizadas para buscar in- formacoes sobre o modulo e procurar o modulo, respectivamente. [-------- Output --------] box72# kldload ./print.ko box72# ./interface 'The Bug! Magazine' The Bug! Magazine [------- /Output --------] [ --- 5. Hooking syscalls Atraves de hooks voce pode ter total controle sobre os valores que sao retorna- dos para o kernel. Supondo que um atacante tem um controle total sobre o sistema(incluindo privi- legios para modificar o sistema), ele pode modificar o kernel usando hooks de forma a manter seu acesso. Todos os recursos oferecidos pelo kernel podem ser usado em beneficio do atacante ou em beneficio do sysadmin. [ --- 5.1. 'ptrace' syscall <---ptrace-hook.c---> static pid_t last_pid = 0; static int ptrace_hook(struct thread *td, void *ptrace_args) { struct ptrace_args *uap; uap = (struct ptrace_args *) ptrace_args; if(last_pid != td->td_proc->p_pid) { last_pid = td->td_proc->p_pid; printf("[%s|%d] UID(%d) EUID(%d)\n", td->td_proc->p_comm, td->td_proc->p_pid, td->td_proc->p_ucred->cr_uid, td->td_proc->p_ucred->cr_gid); } return(ptrace(td, uap)); } static int load(struct module *module, int cmd, void *arg) { int error = 0; switch(cmd) { case MOD_LOAD: sysent[SYS_ptrace].sy_call = (sy_call_t *)ptrace_hook; uprintf("[+] ptrace hook loaded\n"); break; case MOD_UNLOAD: sysent[SYS_ptrace].sy_call = (sy_call_t *)ptrace; uprintf("[-] ptrace hook unloaded\n"); break; default: error = EOPNOTSUPP; break; } return(error); } [-------- Output --------] (tty1) box72# kldload ./ptrace_hook.ko [+] ptrace hook loaded (tty2) $ gdb -q /bin/date (no debugging symbols found)...(gdb) (gdb) r Starting program: /bin/date (no debugging symbols found)... ... Mon Jun 22 00:29:43 BRT 2009 Program exited normally. (gdb) (tty1) [gdb|3155] UID(1001) EUID(1001) [------- /Output --------] [ --- 6. Finalizando... [ --- 6.1. Conclusoes Nao esquecam de ver o uuencode com mais exemplos. Quero mandar um abraco para todos da The Bug! Magazine. Abracos, Spike Spiegel [ --- 6.2. Referencias [0] KLD Examples, /usr/share/examples/kld [1] FreeBSD - Developers Handbook, www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/index.html [3] Joseph Kong, Designing BSD Rootkits: An Introduction to Kernel Hacking, No Starch Press, 2007. [4] Greg Lehey, Debugging Kernel Problems, http://www.lemis.com/grog/Papers/Debug-tutorial/tutorial.pdf begin 644 thebug-0x04-kld.tar.gz M'XL(`*)304H``^U:_V_:.!3?K^2O\+KK+6$!DD!`&D>E7N%VU=%V*M>3IJZ* MTL24B)"@?.G:[?:_W[,3P(0`QXYEZ\T?"9+8SWZ.G]_+\WN.1O@VOJLH#TJC M,G;MVK.O``70TG5R55MZG;W.\$Q5-4W1=%4GY:JBJZUG2/\:@\DB#B,S0*@( M5M\CHHS\IX'C17M>!3O)O]$"^:M-OM/?`@`FXV&FOE MWVS6&?DW0?[U5A/DK^R!]U;\X/*OE6C*P1 MR*)MY!FB24 M2G0I#\6#.#3OX.7>>X0THO3MZ[QW(A.?]M7(CR0@_.)&H2*07TEOU'@>A MXWNH@T+G(_:'(BF%ZA+P);.9XL'E,D!=/'SC(09+VE/NK/-R*P"W;WI M$A8!CN+`$U-"D6F0CDF]D:%AX&)/3)\E]`JI$G3_61#V*O]\_:?_>]%]@BWZ MK^J:GK'_=;6I2X%2D`:.YZO=$.@JP\@D?N&%E(M.7D]'Y5;]/9/JME7H'Y-O_ M,W.,AXZ+]\-CF_^G-+/^?[VN:MS^%X'!Y_0%;_1]AU_K^'7<`V M_U]3U(S\&YJB<_M?!)Z&@[_DO>[@O,YB"#F^Z]QU7?9=B;.X<-5_)QH@(ZH. MS\'/I/[ES&UDFR;^ZG+C-[YO5VX?8616$&-W?2^IHTI;YSNJC*WZ/]V_[^^ M)']B_[6FQN/_A>`)^/];4A3L]H`LW,U1?=@1((0BNW(4V09Y@%%!" M0#8^CV9(-Q89JGGX/9]N$5F?U^?M6W(:?V8FEMF_9(+M9`[96#LSS4\XU/Y= M(M?^[S?\O]W_UYN9[W]=;?#X?R&@_G_RO4_!HRU=S`%(Z5_'\4F!8V1KX__D;Y/W+^NZZH.M__%X%-\F?N_U,T8(O] MUQIJ5OYZ0V]P^U\$=CK_MYS1/\W/%#+E]&@A.3)H3H&)F$<@L8_IZ>?Y MP)YWLA$.*$V2G== MV6&7,[VR-/0Z['I M^U]4_E=5V/A/@^9_H9I__PM`