dimanche 19 juillet 2009

Demenagement.

Comme le titre du post l'indique, j'ai décidé de quitter blogspot ; en effet j'ai eu de très nombreux problèmes de mise en page lors de la rédaction de mes articles, a tel point que la rédaction devenait insupportable.
Résultat, j'ai fais une demande d'hébergement chez tuxfamily ou j'ai installé un Wordpress.
Au final, le nouveau blog me plait, sobre/sombre/sérieux, je continuerais donc mes publications sur celui-ci :
http://0vercl0k.tuxfamily.org/bl0g/

Mettez à jour vos RSS ;).
A la prochaine o/.

mardi 19 mai 2009

Understand phook's internals.

Je me décide finalement à publier un petit quelque chose, malgré tous le taff que les études peuvent me demander en ce moment :).

Entrons dans le vif du sujet, "phook" est le nom donné à un PoC réalisé par Shearer and Dreg concernant une technique de hook peu commune, je dirais même nouvelle.
Un paper à d'ailleurs été publié dans la dernière issues de phrack (65).
Après sa lecture, la mise en place de la technique reste assez floue, je decide donc de partager mon PoC (même pas complet !) ; celui-ci m'a permis de me rendre compte qu'au final la technique est loin d'être parfaite et imprenable.

Pour situer le sujet, les auteurs nous propose de combiner de "l'iat patching", avec leur nouvel technique donc, pour nous assurer un total contrôle des apis.
La nouveauté dans tout cela, c'est qu'avec une simple modification de la table des importations d'un binaire, nous allons contrôler seulement les appels "statiques", ceux qui ne seront pas résolus dynamiquement.
En effet, si dans le cadre d'un hook d'iat nous retrouvons nos fonctions dynamiquement en utilisant GetModuleHandle et GetProcAddress et bien la technique devient inutile.

Nos deux compères nous proposent alors un périple dans le PEB, pour être plus précis, ils nous proposent de falsifier les champs DllBase, EntryPoint et SizeOfImage des structures LDR_DATA_TABLE_ENTRY pointé par chaque entrée de la liste doublement chainée PEB.Ldr.InLoadOrderModuleList.
Voici sa structure :

lkd> dt nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
+0x01c EntryPoint : Ptr32 Void
+0x020 SizeOfImage : Uint4B
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING
+0x034 Flags : Uint4B
+0x038 LoadCount : Uint2B
+0x03a TlsIndex : Uint2B
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 Void
+0x04c PatchInformation : Ptr32 Void

Pour rappel, cette liste stocke les modules chargés par un binaire dans l'ordre de chargement en mémoire, on est donc censé trouvé en premier lieu ntdll.dll bien sur suivit de kernel32.dll etc :

kd> !process 0 0 calc.exe
PROCESS 814ad740 SessionId: 0 Cid: 01a4 Peb: 7ffdb000 ParentCid: 05bc
DirBase: 0d88d000 ObjectTable: e15ccbc0 HandleCount: 37.
Image: calc.exe

kd> .process 814ad740
Implicit process is now 814ad740
WARNING: .cache forcedecodeuser is not enabled
kd> !peb
PEB at 7ffdb000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: No
ImageBaseAddress: 01000000
Ldr 001a1e90
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 001a1f28 . 001a2d18
Ldr.InLoadOrderModuleList: 001a1ec0 . 001a2d08
Ldr.InMemoryOrderModuleList: 001a1ec8 . 001a2d10
Base TimeStamp Module
1000000 3b7d8410 Aug 17 22:52:32 2001 C:\WINDOWS\system32\calc.exe
7c910000 4125331a Aug 20 01:09:14 2004 C:\WINDOWS\system32\ntdll.dll
7c800000 4623a01c Apr 16 18:11:08 2007 C:\WINDOWS\system32\kernel32.dll
7c9d0000 4125330f Aug 20 01:09:03 2004 C:\WINDOWS\system32\SHELL32.dll
77be0000 45d97cd4 Feb 19 11:32:52 2007 C:\WINDOWS\system32\msvcrt.dll
77ef0000 45f030c5 Mar 08 16:50:29 2007 C:\WINDOWS\system32\GDI32.dll
7e390000 45f030c6 Mar 08 16:50:30 2007 C:\WINDOWS\system32\USER32.dll
77da0000 412532e8 Aug 20 01:08:24 2004 C:\WINDOWS\system32\ADVAPI32.dll
[...]

Pour comprendre le principe même de la technique, il faut savoir que l'api GetModuleHandle par exemple, va parser cette liste afin de récupérer l'image base d'une dll par exemple.
Imaginer que l'on echange le champs DllBase de la dll kernel32.dll, avec une dll factice ; et bien un GetModuleHandle("kernel32.dll") nous renverrais l'image base de notre dll factice !
C'est en fait le coeur de la technique, car celle-ci nécessite quelques pré-requis pour sa mise en place.

Notre dll devra donc répondre à quelques exigences, en effet elle devra exporter le même nombre de fonctions que la dll original, avec les mêmes noms de fonctions, ainsi que les mêmes ordinaux.
Le premier problème auquel on est confronté, c'est alors de créer une dll qui exporte les mêmes noms de fonctions que kernel32.dll par exemple, en effet si l'on veut créer une exportation pour GetModuleHandle (par exemple), et bien, bien sur le compilateur nous crie dessus étant donné que cette fonction est déjà définie/déclarée dans les headers windows.

Nos amis utilisent alors des fichiers de définitions (.def) à lier au binaire lors de l'édition des liens (avec mingw c'est l'option --def, et avec vc c'est /DEF:).
Par exemple, pour exporter GetModuleHandleA, nous définirons/déclarerons la fonction comme suit :

DLLEXPORT void GetModuleHandleA_()
{
//do the stuff
}


Et notre fichier de définitions sera comme cela :

LIBRARY kernel32
EXPORTS

GetModuleHandleA=GetModuleHandleA_ @ 29


Ou 29 est l'ordinal d'exportation !

Vous devez commencer à comprendre que notre dll, va être ni plus ni moins qu'une espèce de proxy entre le binaire, et la véritable dll ; cependant si l'appel nous intéresse nous le ré-définirons à notre guise, et nous appellerons l'api originel, à la manière d'un hook donc.

Le second problème, est alors de retrouver l'adresse de cette api originel, pour cela nous utiliserons une macro qui grosso-modo réalisera :

GetProcAddress(GetModuleHandleA("kernel32_.dll"), "NotreApi")

Ou kernel32_.dll est le nom de NOTRE dll, car si vous reflechissez 30 secondes, une fois que le PEB hook aura été réalisé, et bien les images bases auront été echangées ; or il nous faut récupérer l'image base de la véritable dll, ce qui sera donc obtenu avec GetModuleHandleA("Kernel32_.dll") .
La macro proposée par les auteurs est la suivante :

unsigned long tmp;

#define JMP( lib, func ) \
__asm \
( "pushad \n" \
" push edx \n" \
" push %1 \n" \
" call eax \n" \
" pop edx \n" \
" push %2 \n" \
" push eax \n" \
" call edx \n" \
" mov %4, eax \n" \
" popad \n" \
\
: : \
"a" (GetModuleHandle) , \
"g" (lib) , \
"g" (func) , \
"d" (GetProcAddress) , \
"g" (tmp) \
); \
asm ( "jmp %0" : : "g" (tmp) );


Je vous l'accorde ça pique les yeux, c'est très moche, mais ça compile avec mingw (avec l'option de compilation -masm=intel) c'est pour cela que je n'ai pas transposé cette macro à coup de _asm compilable seulement avec vcpp.
La macro réalise donc une sauvegarde des registres avec un pushad/popad afin de préserver l'environnement dont nécessite l'api, nous stockons l'adresse de la véritable api dans une variable, et enfin nous sautons sur l'api.

Cette dernière étape soulève un problème un peu plus important, celui-ci se situe au niveau de la pile et donc des arguments.
Il faut être, dans ce code, capable de fournir une pile propre, et utilisable par l'api et cela sans passer par un prototype spécifique ; en effet toutes les fonctions qui nous intéresserons pas, leurs prototypes seront :

void MaFonction_();

En tant normal, une tel fonction n'est pas censé recevoir de paramètre, cependant une option nous permet de réaliser cela, et donc de trouver une solution à notre problème : il faut compiler avec l'option -fomit-frame-pointer.
C'est en fait, le problème le plus délicat à résoudre dans ce projet, je ne sais pas si il est d'ailleurs possible de procéder autrement, j'ai simplement suivie la technique proposé par nos hookeurs de peb.
J'ai d'ailleurs tenté de compiler le code sans spécifiée l'option, et il semblerait que le tout fonctionne toujours..si quelqu'un peut m'éclairer à ce niveau là :].

Bon, une fois réalisé tout cela, nous obtenons un contrôle sur la résolution dynamique des apis ; si nous voulons obtenir un contrôle total, il faut mettre en place une reconstruction de la table d'importation du binaire, afin de faire pointer les importations de kernel32.dll vers NOTRE dll.

Et enfin, il faut aussi reconstruire les tables des importations des autres modules que notre binaire utilisent, à condition que ceux ci importent des fonctions de kernel32.dll ; tous cela dans le but de les rediriger vers NOTRE dll ! Et dans ce cas là, nous avons le contrôle sur tous les appels fait sur les apis de kernel32.dll.

Maintenant, il reste deux/trois détails à résoudre, en effet si l'on veut pouvoir intercepter tous les appels de fonctions en destination de kernel32.dll, nous devons créer le processus dans un état suspendu, à ce moment là on peut enfiler notre casquette "trappeur de PEB" et corrompre les différents champs.
C'est pour moi le point faible de la technique, en effet il faut pouvoir créer le processus pour pouvoir mettre en place correctement l'attaque.

A présent parlons implémentation, dreg & shearer ont choisis d'injecter un code qui va charger la dll (celle qui va mettre en place le peb hook et la reconstructions des imports), leurs code est plutôt original, il injecte en mémoire une structure formé des opcodes nécessaire au chargement de la dll ; les opcodes sont forgés à la volée : funny :)).

Pour ma part, j'ai tout simplement choisis de créer un thread distant sur la fonction LoadLibraryA afin de chargé ma dll.
Celle-ci se chargera de mettre en place le peb hook, et la reconstructions des importations du binaire, par manque d'envie je n'ai pas codé la reconstructions successive des IATs des différents modules chargés par le binaire.

Concernant leurs PoC, après l'avoir testé il semble défectueux chez moi.
En effet, au lieu d'injecter une dll réalisant le peb hook et le patch des imports, ils ont décidés d'interfacer la mise en place de l'attaque par le biais d'une espece de console.
Vous êtes alors contraint de vous connectez à une socket, on vous propose alors un menu (n'utilisez pas putty :p) ..enfin bon je trouve que le poc est entouré de fonction qui n'apporte pas de réel plus value au projet !

Dans la suite d'outil qu'ils nous proposent on trouve, bien sur,un générateur de code, celui-ci est capable de nous générer le code d'une dll avec les mêmes exportations etc ; celui-ci étant codé en C, j'ai décidé de me coder un petit script python (mon premier !:]]) avec le module pefile.
Cela m'a permis d'appréhender python pour finalement lacher le perl !
Je pense en effet qu'il est plus judicieux d'utilisé un language de script pour réaliser une tache de ce genre !

En lançant mon implémentation, si tout ce passe correctement, vous devriez obtenir un dump du genre :

[3876] [ PebPwnDll implémentation par 0vercl0k, idée original de Shearer & Dreg ].
[3876] [DBG] RtlGetCurrentPeb : 0x7c970e89.
[3876] [DBG] PEB en : 0x7ffdf000.
[3876] [DBG] PEB_LDR_DATA en : 0x361e90.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 361ec0.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 361f18.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 361fc0.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 362060.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 362100.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 3622d0.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 362328.
[3876] [DBG] LDR_DATA_TABLE_ENTRY : 3623c8.
[3876]
[3876] [DBG] LDR_DATA_ENTRY_TABLE de la dll hook : 0x3622d0.
[3876] ImageBase : 0x623c0000
[3876] EntryPoint : 0x623c1000
[3876] SizeOfImage : 0x9f000
[3876]
[3876] [DBG] LDR_DATA_ENTRY_TABLE de la dll hooké : 0x361fc0.
[3876] ImageBase : 0x7c800000
[3876] EntryPoint : 0x7c80b5be
[3876] SizeOfImage : 0x105000
[3876]
[3876] [DBG] Kernel32.dll en 0x623c0000.
[3876] [DBG] Eat dumped !
[3876] [DBG] Patch de l'iat du binaire..
[3876] [DBG] Patch de l'iat du binaire, concernant la dll 'kernel32.dll'.
[3876] [DBG] Patch du binaire terminé.
[3876] [DBG] Attaque complète.


Pour tester la mise en place de la technique, j'ai tout simplement rajouter une MessageBox lors d'un appel à l'api Beep().
Si vous voulez tracer le binaire pour constater le fonctionnement, j'ai glissé une int 3, il vous suffit donc de définir OllyDbg comme JIT debugger.


Maintenant, on peut s'interroger si la technique est utilisable dans le cadre d'un malware..j'aurais tendance à dire oui bien sur, seulement un peb hook reste très simple à détecter : il suffit de constater les champs DllBase/EntryPoint/SizeOfImage en mémoire, et ceux du binaire en dur. On se rend vite compte que sa mise en place est aussi contraignante, car beaucoup d'actions sont réalisées, et que plus il y a d'actions à réalisées, plus il y a de chances pour que l'attaque tombe à l'eau (si l'une d'entres elles echoue, c'est la technique entière qui se voit tomber en miettes).

Cependant, une technique de ce type peut-être intéressante pour virtualiser certaines apis (les auteurs en parlent en fin de leur article) ou encore pour réaliser un traceur d'api ; c'est peut-être un peu lourd je vous l'accorde.
Enfin bref, c'est vraiment un article que j'ai A.D.O.R.E, jouer comme cela avec des structures accessible en userland c'est vraiment des techniques que je kiffe :).

Il est temps de lacher les sources, pour cette fois je vous fait une archive contenant source et binaire :

UnderstandPhooksInternals.zip

Sinan voici quelques liens en vrac :
Voilà, j'en ai finis pour aujourd'hui, en espérant que ça vous aura plu ;).


samedi 21 mars 2009

Flirt with the session space.

Je vais aujourd'hui vous parlez d'un espace un peu spécial situé dans le kernel land ; j'ai du mener de longues recherches, des nuits de reverses et j'en passe :).
Tous cela parce qu'il s'agit d'un sujet qui au final reste très très peu documenté, on trouve très peu de précision et lorsque le sujet est abordé dans certains article, les descriptions sont plutôt sommaire.

Le session space, est en fait un espace session (comme son nom l'indique) ; comme vous le savez les OS modernes sont tous capables de gérer les sessions multiples ; et bien windows dédie un espace privé par session !
Une session est en fait un espace privé reservé pour chaque utilisateur par exemple, le fait que plusieurs utilisateurs puissent se connecter en même temps implique alors le cloisonnement de celles-ci !

En fait, tous commence lorsque je me mis à rédiger un Proof of concept concernant la technique de xrayn. Celle-ci permettait de cacher ses hooks dans la SSDT/SSDT Shadow ; 90% environ des anti-rootkits étaient mis à l'echec !
A première vue, le code ne devrait pas trop me poser de problèmes..jusqu'au moment où je dois réaliser une copie de la SSDT Shadow.
En bref la SSDT Shadow est la SSDT pour les threads qui vont gérer une interface graphique ; la SSDT shadow se décompose en deux parties : la première qui est la SSDT que l'on connait bien (dite SSDT system), et la seconde est la SSDT gui.
Un petit dump du kd pour vous montrez ça :

kd> dd nt!KeServiceDescriptorTable l 0x8 80559b80 804e2d20 00000000 0000011c 804d8f48 80559b90 00000000 00000000 00000000 00000000 kd> dd nt!KeServiceDescriptorTableShadow l 0xC 80559b40 804e2d20 00000000 0000011c 804d8f48 80559b50 bf999400 00000000 0000029b bf99a110 80559b60 00000000 00000000 00000000 00000000
Les threads gui peuvent alors gérer les syscalls système, et les syscalls gui sans aller chercher les fonctions dans une autre table.
Sans rentrer dans les détails de la technique (un article sortira bientôt concernant cette technique), lorsque que mon driver tentait d'accéder au tableau de pointeurs sur les fonctions syscalls de win32k et ben BIM BOOOM %*&1 écran bleu !

Je sors alors mon kd :

kd> dd nt!KeServiceDescriptorTableShadow l 0x8
80559b40 804e2d20 00000000 0000011c 804d8f48
80559b50 bf999400 00000000 0000029b bf99a110

kd> dds poi(nt!KeServiceDescriptorTableShadow+0x4*4)
bf999400 ????????
bf999404 ????????
bf999408 ????????
bf99940c ????????
bf999410 ????????
bf999414 ????????
bf999418 ????????
bf99941c ????????
bf999420 ????????
bf999424 ????????

Hmm l'espace mémoire parait comme innexistant, et c'est là que le problème s'esquisse.
Tout ça parait plutôt floue, j'ai donc potasser un peu les Windows internals, c'est donc à ce moment que je découvre le session space !
On peux y lire :

Each session has a session-specific paged pool area used by the kernel-mode portion of the Windows subsystem (Win32k.sys) to allocate session-private GUI data structures.
In addition, each session has its own copy of the Windows subsystem process (Csrss.exe) and logon process (Winlogon.exe).
The session manager process (Smss.exe) is responsible for creating new sessions, which includes loading a session-private copy of Win32k.sys,
creating the session-private object manager namespace, and creating the session-specific instances of the Csrss and Winlogon processes.

When a process is created, this range of addresses is mapped to the pages appropriate to the session that the process belongs to.


Nous voilà avec quelques pistes, on se rend compte alors que la Shadow n'est accessible seulement aux processus qui appartiennent à la dîtes session ; autrement dit tous les threads de ces processus auront accès à cet table.

Cet à ce moment qu'il faut réfléchir un peu si l'on veut dégoter quelques informations intéressante ; on pourrait déjà regarder du coté du gestionnaire de sessions : smss.exe, ensuite du driver win32k.sys ..j'ai donc essayé de les analyser à la recherche d'infos plus ou moins pertinente.
Smss.exe se charge à son initialisation de charger le driver win32k.sys :

Main!smss.exe
-> smss!SmpInit
-> smss!SmpLoadDataFromRegistry
-> smss!SmpLoadSubSystemsForMuSession

.text:485887B2 push offset aSystemrootSyst ; "\\SystemRoot\\System32\\win32k.sys"
.text:485887B7 lea eax, [ebp+DestinationString]
.text:485887BA push eax ; DestinationString
.text:485887BB call ds:__imp__RtlInitUnicodeString@8 ; RtlInitUnicodeString(x,x)
.text:485887C1 push 8
.text:485887C3 lea eax, [ebp+DestinationString]
.text:485887C6 push eax
.text:485887C7 push 26h
.text:485887C9 call edi ; NtSetSystemInformation(x,x,x) ; NtSetSystemInformation(x,x,x)
Smss.exe utilise une technique peu rencontré pour charger le driver, certains parle de "quick an dirty-way" (cf Suberving the windows kernel). Il suffit d'appeler NtSetSystemInformation avec l'argument SystemLoadAndCallImage ainsi que le full path du driver.
On parle de dirty way, tout simplement parce que le driver sera chargé dans une zone mémoire swappable, pour plus d'information chapitre 2 du bouqin :).

Une fois le driver lancé, celui-ci va ajouter la SSDT shadow :

INIT:BF9AFF13 push offset _W32pArgumentTable
INIT:BF9AFF18 push _W32pServiceLimit
INIT:BF9AFF1E mov _countTable, esi
INIT:BF9AFF24 push esi
INIT:BF9AFF25 push offset _W32pServiceTable
INIT:BF9AFF2A call ds:__imp__KeAddSystemServiceTable@20 ; KeAddSystemServiceTable(x,x,x,x,x)

.data:BF999B00 _W32pServiceTable dd offset _NtGdiAbortDoc@4
.data:BF999B04 dd offset _NtGdiAbortPath@4 ; NtGdiAbortPath(x)
.data:BF999B08 dd offset _NtGdiAddFontResourceW@24 ; NtGdiAddFontResourceW(x,x,x,x,x,x)
.data:BF999B0C dd offset _NtGdiAddRemoteFontToDC@16 ; NtGdiAddRemoteFontToDC(x,x,x,x)
.data:BF999B10 dd offset _NtGdiAddFontMemResourceEx@20 ; NtGdiAddFontMemResourceEx(x,x,x,x,x)
.data:BF999B14 dd offset _NtGdiRemoveMergeFont@8 ; NtGdiRemoveMergeFont(x,x)
.data:BF999B18 dd offset _NtGdiAddRemoteMMInstanceToDC@12 ; NtGdiAddRemoteMMInstanceToDC(x,x,x)
.data:BF999B1C dd offset _NtGdiAlphaBlend@48 ; NtGdiAlphaBlend(x,x,x,x,x,x,x,x,x,x,x,x)
[...]

.data:BF99A810 _W32pArgumentTable db 4 ; DATA XREF: DriverEntry(x,x)+E4o
.data:BF99A811 db 4
.data:BF99A812 db 18h
.data:BF99A813 db 10h
.data:BF99A814 db 14h
.data:BF99A815 db 8
.data:BF99A816 db 0Ch
.data:BF99A817 db 30h
[...]
Avec ce session space il y a donc une histoire d'espace qui est accessible/existe, dans certains contexte ; en fait tous les processus d'une session voient cette table, seul system ne peux y accéder ; par analogie c'est comme un processus avec ses dlls, system serait un processus sans la dll Session space (c'est bien sûr une analogie, soyons d'accord :)) .

On peut verifier tout ça avec le kd ; en premier lieu se placer dans le contexte du processus System (le cas de notre driver donc), et ce placer dans le contexte d'un tout autre processus :

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 817cc7c0 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1001cb0 HandleCount: 206.
Image: System
[..]
kd> .process /i 817cc7c0
You need to continue execution (press 'g' ) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
804e3b25 cc int 3
kd> dds poi(nt!KeServiceDescriptorTableShadow+0x4*4)
bf999400 ????????
bf999404 ????????
bf999408 ????????


kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
[..]
PROCESS
81549b28 SessionId: 0 Cid: 05a8 Peb: 7ffdf000 ParentCid: 0588
DirBase: 093c9000 ObjectTable: e1a74620 HandleCount: 346.
Image:
explorer.exe
[..]
kd> .process /i
81549b28
You need to continue execution (press 'g' ) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
804e3b25 cc int 3
kd> dds poi(nt!KeServiceDescriptorTableShadow+0x4*4)
bf999400 bf9357a3 win32k!NtGdiAbortDoc
bf999404 bf947361 win32k!NtGdiAbortPath
bf999408 bf896625 win32k!NtGdiAddFontResourceW
bf99940c bf93ef25 win32k!NtGdiAddRemoteFontToDC
bf999410 bf948978 win32k!NtGdiAddFontMemResourceEx
[..]
J'ai donc réaliser une fonction qui permettait ça copie, celle-ci fonctionne plutôt simplement, je m'attache au processus explorer.exe (Il faut s'attacher à un processus qui sera lancé sur le systeme ; j'ai pris explorer.exe comme j'aurais pu prendre calc.exe!), et réalise la copie normalement (vous verrez alors que mon ancienne technique qui utilisait un apc kernel envoyé sur un thread gui était bien trop compliqué pour pas grand chose :).

Sinon, on pourrait s'occuper de mapper win32k.sys dans l'espace du driver en utilisant son adresse physique avec des apis comme MmMapIoSpace(), il suffirait ensuite de parser le pe du binaire, pour atterrir dans la section .data ; où est stocké la SSDT Shadow.

.data:BF999C00 ; Segment type: Pure data
.data:BF999C00 ; Segment permissions: Read/Write
.data:BF999C00 _data segment para public 'DATA' use32
.data:BF999C00 assume cs:_data
.data:BF999C00 ;org 0BF999C00h
.data:BF999C00 _W32pServiceTable dd offset _NtGdiAbortDoc@4
.data:BF999C00 ; DATA XREF: DriverEntry(x,x)+F6o
.data:BF999C00 ; NtGdiAbortDoc(x)
.data:BF999C04 dd offset _NtGdiAbortPath@4 ; NtGdiAbortPath(x)
.data:BF999C08 dd offset _NtGdiAddFontResourceW@24 ; NtGdiAddFontResourceW(x,x,x,x,x,x)
.data:BF999C0C dd offset _NtGdiAddRemoteFontToDC@16 ; NtGdiAddRemoteFontToDC(x,x,x,x)
.data:BF999C10 dd offset _NtGdiAddFontMemResourceEx@20 ; NtGdiAddFontMemResourceEx(x,x,x,x,x)
.data:BF999C14 dd offset _NtGdiRemoveMergeFont@8 ; NtGdiRemoveMergeFont(x,x)
.data:BF999C18 dd offset _NtGdiAddRemoteMMInstanceToDC@12 ; NtGdiAddRemoteMMInstanceToDC(x,x,x)
.data:BF999C1C dd offset _NtGdiAlphaBlend@48 ; NtGdiAlphaBlend(x,x,x,x,x,x,x,x,x,x,x,x)
.data:BF999C20 dd offset _NtGdiAngleArc@24 ; NtGdiAngleArc(x,x,x,x,x,x)
.data:BF999C24 dd offset _NtGdiAnyLinkedFonts@0 ; NtGdiAnyLinkedFonts()
.data:BF999C28 dd offset _NtGdiFontIsLinked@4 ; NtGdiFontIsLinked(x)
.data:BF999C2C dd offset _NtGdiArcInternal@40 ; NtGdiArcInternal(x,x,x,x,x,x,x,x,x,x)
.data:BF999C30 dd offset _NtGdiBeginPath@4 ; NtGdiBeginPath(x)

Un peu tricky, mais ça se tiens, seul problème c'est la translation de l'adresse Virtuelle à l'adresse Physique, étant donné que le driver ne voit pas l'espace en question, l'adresse physique ne peut être récupérer avec MmGetPhysicalAddress() (l'idéal serait de ne pas s'attacher à un processus).


Bilan de ce post, un peu décevant, mes recherches ont été longues pour au final pas grand chose..m'enfin bon :].
Sinan, je vous recommande le blog de bons amis, Futurezone ; le blog est mis à jour regulierement avec des articles très clean et tout, c'est que du bonheur quoi.


Voilà pour aujourd'hui :
CopySSDTShadow.c

Je compte aussi sortir une version un peu plus propre de HC-L, en effet j'ai décidé d'embarquer le NDK, de séparer les en-têtes de fonctions des définitions, ajouter des fonctions etc..!