samedi 6 septembre 2008

Overclok owned :p - List Hidden Processes

Bonjour/Bonsoir,
nouvelle couleur (oui je sais j'ai un peu fais n'importe quoi, je m'amuse ^^), nouveau départ. En effet voilà bien longtemps que je n'avais pas posté ici, et je me vois obligé de laisser un petit post en réponse à celui d'Overclok, je cite : "How to pwn lilxam's toolz :)". Ce post faisant lui-même référence à mon article " Listing all processes". Bref revoyons un peu tout ça :
-Ivanlef0u puis Overclok s'amuse à cacher des processus à "unlinkant" les structure EPROCESS de ces processus de la table PsActiveProcessList.
-Me viens alors l'idée de trouver un moyen de retrouver ces processus cachés, je met alors en place un Brute Force sur les ID des processus en testant leur existence via OpenProcess(). Rien de bien élégant mais ceci à le mérite de fonctionner.
-Overclok viens alors à présenter 2 autres techniques pour cacher des processus. La première étant l'unlink de la PspCidTable, technique qui met en échec mon précédent tool puisque la fonction OpenProcess() se sert de la table PspCidTable. La seconde est l'unlink d'une structure dans csrss.exe. Ce processus étant un sorte de "sous-système", il a un handle ouvert sur tous les processus. On aurait donc pu lister les processus en récupérant tous les handles ouvert par csrss.exe.

Mon problème alors est d'arriver à lister tous les processus mêmes cachés avec les trois techniques précédentes.

The HandleTableList



Au cours de ma recherche je me suis beaucoup penché sur les handles et l'Object Manager jusqu'à apprendre l'existence d'une table appelé ObjectTable contenue dans chaque processus contenant les handles ouverts par celui-ci. Ces tables sont de type HANDLE_TABLE :


kd> 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



A noter le pointeur sur la structure EPROCESS en +0x04 et le pointeur sur l'ID du processus en +0x08. Mieux encore, les HANDLE_TABLE des processus forment une liste double-chainée. On note en effet le membre de la structure HandleTableList qui est de type LIST_ENTRY :

kd> dt nt!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY


On peut accéder à l'ObjectTable d'un processus par le biais de la structure EPROCESS :

kd> dt nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
...


En +0x0C4 se trouve un pointeur sur notre ObjectTable.
Voici alors les étapes qu'il nous faudrait suivre :
1. Récupérer l'EPROCESS courante ou d'un processus connu.
2. Récupérer un pointeur sur l'ObjectTable.
3. Parcourir la liste chainée des ObjectTable de chaque processus.
4. A chaque ObjectTable récupérer l'ID du processus ainsi qu'un pointeur sur l'EPROCESS.
5. Récupérer le nom du processus grâce à cette dernière (+0x174 ImageFileName : [16] UChar).

Cependant j'ai pensé affirmer un peu vite que les ObjectTable forment une liste chainée. J'ai alors voulu vérifier ceci avant de me mettre à coder ce driver (/me redoute encore le r0), c'est possible avec windbg :

Etape 1 :

kd> !process csrss.exe
PROCESS 80558e80 SessionId: none Cid: 0000 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1001cd8 HandleCount: 173.
Image: Idle
VadRoot 00000000 Vads 0 Clone 0 Private 0. Modified 0. Locked 0.
...


Avec cette commande on peut obtenir un pointeur sur l'EPROCESS à l'adresse 0x80558e80 (et même sur l'ObjectTable mais on va faire comme ci on avait pas vu, pour suivre nos étapes).

Voyons ensuite à quoi ressemble l'EPROCESS de csrss.exe :

kd> dt nt!_EPROCESS 80558e80
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x0
+0x078 ExitTime : _LARGE_INTEGER 0x0
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : (null)
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x090 QuotaUsage : [3] 0
+0x09c QuotaPeak : [3] 0
+0x0a8 CommitCharge : 0
+0x0ac PeakVirtualSize : 0
+0x0b0 VirtualSize : 0
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x0bc DebugPort : (null)
+0x0c0 ExceptionPort : (null)
+0x0c4 ObjectTable : 0xe1001cd8 _HANDLE_TABLE
...


Etape 2:
On peut alors récupérer un pointeur sur l'objectTable comme prévu à l'adesse 0xe1001cd8.

Voyons à quoi celli-ci ressemble :

kd> dt nt!_HANDLE_TABLE e1001cd8
+0x000 TableCode : 0xe1002000
+0x004 QuotaProcess : (null)
+0x008 UniqueProcessId : 0x00000004
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe10065d4 - 0x805617c8 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0
+0x030 FirstFree : 0x520
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 173
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0


Etape 3 :
On se penche ensuite sur l'HanldeTableList :


kd> dt nt!_LIST_ENTRY e1001cd8+0x01c
[ 0xe10065d4 - 0x805617c8 ]
+0x000 Flink : 0xe10065d4 _LIST_ENTRY [ 0xe13d66fc - 0xe1001cf4 ]
+0x004 Blink : 0x805617c8 _LIST_ENTRY [ 0xe1001cf4 - 0xe1661d9c ]


On a en théorie un pointeur sur l'HandleTableList précédente et suivante.
Récupérons la suivante :

kd> dt nt!_LIST_ENTRY 0xe10065d4
[ 0xe13d66fc - 0xe1001cf4 ]
+0x000 Flink : 0xe13d66fc _LIST_ENTRY [ 0xe13c7fd4 - 0xe10065d4 ]
+0x004 Blink : 0xe1001cf4 _LIST_ENTRY [ 0xe10065d4 - 0x805617c8 ]


Etape 4:
Tout semble correct pour l'instant, tentons alors de récupérer l'ObjectTable :

kd> dt nt!_HANDLE_TABLE e10065d4-0x01c
+0x000 TableCode : 0xe128e000
+0x004 QuotaProcess : 0x8163d1a0 _EPROCESS
+0x008 UniqueProcessId : 0x000001ac
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe13d66fc - 0xe1001cf4 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0
+0x030 FirstFree : 0x54
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 19
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0


Tout à l'air bon, le PID de ce processus est 0x1ac.

Etape 5 :

Voyons à qui il appartient :

kd> dt nt!_EPROCESS 0x8163d1a0
...
+0x084 UniqueProcessId : 0x000001ac
...
+0x174 ImageFileName : [16] "smss.exe"


Oh mais que voyons ? Il s'agit bien d'un processus :))). Notre hypothèse était donc bonne :).
Et bien voilà pour cette technique, pour le code je vous envoi à la fin de l'article.
Voici un petit exemple de ce que ça donne, ayant caché au préalable calc.exe avec le driver d'Overclok :


Lilxam driver OK

Process : System

Process : smss.exe

Object Table : 0xe145e178

--- PID : 0x370

--- PPEPROCESS : 0xe145e17c

--- PEPROCESS : 0x86071980

--- Name : csrss.exe

Object Table : 0xe1014bb0

--- PID : 0x33c

--- PPEPROCESS : 0xe1014bb4

--- PEPROCESS : 0x860754b8

--- Name : smss.exe

Object Table : 0xe1003ec8

--- PID : 0x4

--- PPEPROCESS : 0xe1003ecc

--- PEPROCESS : 0x0

Object Table : 0x80564b8c

--- PID : 0x0

--- PPEPROCESS : 0x80564b90

--- PEPROCESS : 0x0

Object Table : 0xe134ed10 <---- :))))

--- PID : 0x260

--- PPEPROCESS : 0xe134ed14

--- PEPROCESS : 0x85706a78

--- Name : calc.exe

...

End



Malheureusement cette technique est facilement bypassable et Overclok me l'a déjà prouvé :(. En revanche rkUnhooker arrive encore à trouver le processus d'Overclok...
J'ai alors encore un petite hypothèse que je n'est pas réussi à mettre en place.


Getting list of threads ???




Dans son article, Overclok se pose plusieurs question après avec tenté l'unlink de la structure ETHREAD, je cite :

"
- 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 ?
"

Mes recherches et mes tentatives mon alors amené sur une table, appelé KiWaitListHead sous XP qui, à ce qui ce dirait, permettrait d'obtenir une liste des threads.
Alors voyons comment trouver cette liste. Après quelques recherches j'apprends que cette table est utilisée par la fonction KeWaitForSingleObject. Un petit disass avec IDA, j'ai préféré l'utilisé à windbg pour pouvoir voir tous les jumps facilement car celui-ci m'a trompé pendant un moment, et on trouve :

loc_40DE4F:
mov ecx, ds:dword_48226C
lea eax, [esi+60h]
mov dword ptr [eax], offset _KiWaitListHead
mov [eax+4], ecx
mov [ecx], eax
mov ds:dword_48226C, eax
jmp loc_40513E


+0x8CB8 mov dword ptr [eax], offset _KiWaitListHead



On va donc scanner cette fonction et on pourra récupérer un pointeur sur la table :).
Mais voilà, une fois la table récupérée je ne sais plus quoi en faire, je n'arrive pas à reconstituer les structures :(.
Donc voilà j'en appelle à vous, peut-être avez-vous une idée ?
A suivre...

Je vous propose donc seulement le code pour récupérer un pointeur sur KiWaitListHead.

Les codes sont dispo ici


Merci à Overclok pour cette petite aventure bien sympatique, peut-être n'est-elle pas finie ;).

Lilxam.

2 commentaires:

Anonyme a dit…

Yo, je crois que le code de cet article te sera utile : http://www.rootkit.com/newsread.php?newsid=902

Bonne continuation :]
Et n'oubliez pas que je vous surveille !

lilxam a dit…

Yop ivanlef0u :)
merci pour le code, en effet il contient l'info qu'il me manquais à savoir l'offset 0x60 (pour XP) grâce auquel je peux retrouver l'ETHREAD. Je vais donc pouvoir continuer le code dans un prochain article :)

Bonne continuation à toi aussi :)
Lilxam.