Un titre un peu provacateur, mais rien de méchant, juste un petit jeu entre lilxam et moi ; autrement dit mon article n'est pas là pour dénigrer son travail ou autre
bien au contraire.
Les avertissements étant fait, rentrons dans le vif du sujet .. :).
Il y a quelques temps, j'ai entamé l'écriture d'un petit post concernant une technique de
DKOM aboutissant à caché un processus (plus ou moins bien justement).
Le noyau possède un chainage de structures de type
EPROCESS (par une liste doublement chainé à l'offset 0x088), il était donc possible d'unlinker une structure en particulier.
Le processus se retrouvait alors protégé de l'énumération par le
taskmgr.exe ou encore
ProcessExplorer.
C'est à présent que lilxam entre en jeu.
Peu de temps après mon petit Proof of Concept, il met en place une technique
userland permettant d'énumérer les processus malgré la modification des structures en mémoire.
Cet technique en question, très ingénieuse au passage, permettant de récupérer le nom des processus en bruteforçant les
PIDs de ceux-ci.
Seulement, tout cela m'interpelle, comment était-il possible de retrouver ce processus, alors qu'il n'était plus présent dans les structures
EPROCESS ..?
La réponse ne devrait pas être trop complexe à rechercher ; en effet un commentaire de
Ivanlef0u nous donne de précieuses informations : il aborde la présence d'une mystérieuse table prénommé
PspCidTable référençant des informations concernant les processus à savoir une translation entres
PIDs/
TIDs et objet
EPROCESS/
ETHREAD.
Nous voilà sur la bonne route, sortons IDA afin d'analyser les apis utilisées dans le code de lilxam ; le réel problème étant de savoir comment le système se débrouille pour trouver le processus en question, désassemblons un peu
OpenProcess exporté par kernel32.dll.
Nous suivons en premier lieu les appels successif à savoir:
->
OpenProcess-->
NtOpenProcess--->
ZwOpenProcessNous savons bien que le système afin de permettre l'appel d'un syscall va passer par la
SSDT pour retrouver l'adresse de la fonction associé à son numéro de syscall (
0x7a pour NtOpenProcess).
Balayons alors ce code à la recherche de la translation
PID/
TID en objet
EPROCESS/
ETHREAD :
PAGE:004A9C92 cmp [ebp+var_1A], 0PAGE:004A9C96 jnz loc_52ADDFPAGE:004A9C9C cmp [ebp+var_19], 0PAGE:004A9CA0 jz loc_50F76DPAGE:004A9CA6 mov [ebp+var_30], esiPAGE:004A9CA9 cmp [ebp+var_28], esiPAGE:004A9CAC jnz loc_4D88A0PAGE:004A9CB2 lea eax, [ebp+var_24]PAGE:004A9CB5 push eaxPAGE:004A9CB6 push [ebp+var_2C]PAGE:004A9CB9 call _PsLookupProcessByProcessId@8L'appel à PsLookupProcessByProcessId est plus que révélateur, son nom en dit assez large sur son rôle et de plus cette fonction est renseigné par la msdn.Désassemblons cette dernière fonction, notre réponse y est surement caché :).PAGE:004A9B25 mov edi, edi
PAGE:004A9B27 push ebp
PAGE:004A9B28 mov ebp, esp
PAGE:004A9B2A push ebx
PAGE:004A9B2B push esi
PAGE:004A9B2C mov eax, large fs:124h
PAGE:004A9B32 push [ebp+arg_0]
PAGE:004A9B35 mov esi, eax
PAGE:004A9B37 dec dword ptr [esi+0D4h]
PAGE:004A9B3D push _PspCidTable
PAGE:004A9B43 call _ExMapHandleToPointer@8
PAGE:004A9B48 mov ebx, eax
PAGE:004A9B4A test ebx, ebx
Mais que voyons nous ; notre"PspCidTable".Arrivé ici nous comprenons alors que les apis qu'utilise notre très cher lilxam ; en particulier OpenProcess se base donc sur cette fameuse table pour retrouver notre processus à partir de son PID.Sans plus attendre je vous recommande (encore) un article de mon noble jedi ; celui-ci traite de long en large les tables de type HANDLE_TABLE au prototype suivant : lkd> dt nt!_HANDLE_TABLE
+0x000 TableCode : Uint4B
+0x004 QuotaProcess : Ptr32 _EPROCESS
+0x008 UniqueProcessId : Ptr32 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
+0x02c ExtraInfoPages : Int4B
+0x030 FirstFree : Uint4B
+0x034 LastFree : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount : Int4B
+0x040 Flags : Uint4B
+0x040 StrictFIFO : Pos 0, 1 Bit
Ce type de table est d'ailleurs presque omni-présent dans les processus ; les
handles que gèrent un processus sont stockés dans une table de ce genre.
Celle-ci est accessible à l'offset
0x0c4 :
lkd> dt nt!_EPROCESS
[...]
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
A présent, parlons
PspCidTable.
En glanant sur le net, on s'aperçoit assez rapidement qu'il y a quelques années la modification
de cet table était une technique à la pointe dirais-je.
Cependant, je me suis vite rendu compte que mes parents ont entrepris ma construction trop tard :p ; en effet mon implémentation ce chargeait d'enlever l'objet
EPROCESS du processus à caché, mais malgrès cela
RkUnhooker, anti-rootkit digne de ce nom, trouve toujours le moyen de retrouver l'objet
EPROCESS, et donc de permettre le kill de celui-ci :/.
Un peu frustré, je commence à m'interroger sur la technique que
RkUnhooker pourrait déployer afin de retrouver ce maudit processus.
En analysant la définition de la structure
ETHREAD on peut remarquer qu'il existe un lien avec la structure
EPROCESS associé, à l'offset
0x220:
lkd> dt nt!_ETHREAD
[...]
+0x220 ThreadsProcess : Ptr32 _EPROCESS
; on ne peut que se demander
si
RkUnhooker ce focalise sur les
threads, et remonte au processus par le biais de divers fonctions.
Me voilà repartit dans cette table, afin d'enlever les objets
ETHREADs .. après l'implémentation, à ma grande surprise le processus devient inactif ;
zombie...
C'est donc à ce moment là que les questions jaillissent à travers mon esprit :
- Comment
RkUnhooker remet la main sur mon processus ?
- Quel(s) technique(s) utilise t-il pour cela ?
- Existe t-il un autre endroit où une liste des processus lancées sur le système est disponible ?
Voilà concernant la
PspCidTable, ma fonction permet alors de supprimer l'entrée concernant le processus à caché dans la table ; l'outil de lilxam est donc finalement inefficace sur ce coup là .:p.
Comme je le racontais plus haut, cette implémentation est trop maigre pour permettre de leurrer
RkUnhooker.
C'est donc ainsi que commence mes recherches sur les techniques existantes permettant la protection d'un processus de l'énumération. Après quelques recherches sur la toile, mon regard s'attarde sur cette
technique ; en effet le processus vital
csrss.exe contiendrait une liste des processus lancée sur le système.
L'auteur de la technique explique très bien les faits et son analyse est détaillée ; en quelques mots, le processus tiens à jour une liste des processus lancés sur le système sous forme de liste chainée.Il est alors aisé pour nous de pratiquer un
unlink ..
Pour un projet personnel, j'ai décidé de porter cette unlink en
ring0 ; celle-ci devient alors tout de suite bien plus
intéréssante car on est obligé de faire appel à pleins de petites choses pour arrivé à nos fin : du parcours de
PE, de l'attachement à l'
user-space d'un processus et j'en passe.
Les liens :
-
PspCidTable PoC-
UnlinkListEntryInCsrssSinan si vous ne savez pas quoi faire, je vous conseil d'aller faire un tour sur le site de la
F.A.T.
Cette petite
team aux membres bien sympathiques publient des codes et papers vraiment géniaux ( je pense au dernier que j'ai lu ; autrement dit le paper concernant
win32.leon par
kaze ) ..enfin bref à bookmarker pour ceux qui ne connaissent pas :).
Et si vous en voulez encore un peu, je vous conseil de lire les derniers articles de deux de mes amis :
-Celui de
rAsM ; monsieur met en place une technique pouvant servir de base à un potentiel
keylogger kernel (a condition de patcher les
BSODs :))).
-Celui de
Ivanlef0u ; mon
s1ths préféré propose un petit article concernant le
subsystem csrss en abordant notament les processus consoles.
Pour clore ce petit post, un dernier
lien ; celui-ci vient tout droit des
dragons asiatiques ..
Sudami reverseur de talent continus de nous faire réver en nous proposant de nouvelles techniques de
DKOMs ; celle-ci aborde le hook d'une fonction stockée dans la structure liée aux
Objects Types Initializers ( ..qui m'avait mis en échec
ici ).
Bien evidement, raffolant de ce genre de technique je m'empresse de mettre en place un petit driver capable de placer ce hook mais c'est (du moins chez moi (XP SP2 FR)) un
échec.. la routine est apparement bien appelé par le
system mais en aucun cas elle empèche la fermeture du processus.
Peut-être que
sudami explique les limites de sa technique dans son article, ou que celle-ci est valable avec des
pré-requis.. Enfin bon si quelqu'un en sait plus qu'il me contacte :).
Voilà pour ce petit article, en espérant que je vous aurrez appris quelque(s) chose(s),
bonne journée à vous.
PS :
Bonne rentrée à ceux qui ne l'ont toujours pas faite.