Archive for avril, 2007
Vous l’attendiez, le voilà ! Le POC de mon précédent post, ou l’art de planquer des fichiers en utilisant un IRP Hook. Mais avant que vous fassiez des bêtises, on va mettre au clair quelques petites choses.
Pour récupérer le contenu d’un dossier, l’OS va utiliser un appel système sur l’API NtQueryDirectoryFile. Cette API va ensuite forger une requête, un IRP, et l’envoyé au driver Nfts qui se chargera de répondre. La routine du driver Nfts qui est utilisée s’appelle NtfsFsdDirectoryControl de prototype :
NTSTATUS MyNtfsFsdDirectoryControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
Les arguments sont passés à travers l’IRP. Par exemple, lorsque vous affichez le contenu de votre C:\, voici ce qui se passe.
Breakpoint 1 hit
Ntfs!NtfsFsdDirectoryControl:
fcb3f2c3 684c010000 push 14Ch
kd> kb
ChildEBP RetAddr Args to Child
f9316cd0 804e3d77 80e7a408 ffacb430 80e8cf38 Ntfs!NtfsFsdDirectoryControl
f9316ce0 fcbbb459 f9316d0c 804e3d77 80e7add0 nt!IopfCallDriver+0x31
f9316ce8 804e3d77 80e7add0 ffacb430 806ed070 sr!SrPassThrough+0x31
f9316cf8 8056a9ab f9316d64 00c2de3c 80574dad nt!IopfCallDriver+0x31
f9316d0c 80574e0a 80e7add0 ffacb430 80e39118 nt!IopSynchronousServiceTail+0x60
f9316d30 804df06b 00000544 00000000 00000000 nt!NtQueryDirectoryFile+0x5d
f9316d30 7c91eb94 00000544 00000000 00000000 nt!KiFastCallEntry+0xf8
00c2de04 7c91df6a 7c80eec2 00000544 00000000 ntdll!KiFastSystemCallRet
00c2de08 7c80eec2 00000544 00000000 00000000 ntdll!ZwQueryDirectoryFile+0xc
00c2e114 7c9f8afd 00c2e3bc 00000000 001500ac kernel32!FindFirstFileExW+0x3a0
00c2e138 7c9f8a97 00c2e3bc 001500ac 0014fe9c SHELL32!SHFindFirstFile+0x2a
00c2e38c 7c9fa996 00100138 00000000 00c2e3bc SHELL32!SHFindFirstFileRetry+0x5b
00c2e5dc 7c9fa870 00101a88 00101a70 00c2e604 SHELL32!CFileSysEnum::Init+0x14b
00c2e5ec 7c9fab6d 0010c820 00100138 00000060 SHELL32!CFSFolder_CreateEnum+0x37
00c2e604 7c9ff03d 0010c830 00100138 00000060 SHELL32!CFSFolder::EnumObjects+0x30
00c2e638 7c9ffa3e 00100138 0014b7f4 0014b7d8 SHELL32!CDefviewEnumTask::FillObjectsToDPA+0x8b
00c2e68c 7774e201 00000001 00000000 0014b7d8 SHELL32!CDefView::CreateViewWindow2+0x2de
WARNING: Frame IP not in any known module. Following frames may be wrong.
00c2e738 777392ca 000dac20 0014b7d8 0014e210 0x7774e201
00c2e848 77f5a77f 00106900 777364e1 0013fac4 0x777392ca
00c2e77c 75f35fa2 000dac24 0014e210 00145798 SHLWAPI!EnumConnectionPointSinks+0xac
Dans la call stack on peut retrouver les arguments passé à la fonction NtQueryDirectoryFile :
kd> dd 00c2de08+8
00c2de10 00000544 00000000 00000000 00000000
00c2de20 00c2de78 00c2dea8 00000268 00000003
00c2de30 00000001 00c2de90 00000000
Ce qui correspond, pour être plus clair à l’appel suivant :
ZwQueryDirectoryFile(
IN HANDLE FileHandle, -> 0x544
IN HANDLE Event OPTIONAL, -> NULL
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, -> NULL
IN PVOID ApcContext OPTIONAL, -> NULL
OUT PIO_STATUS_BLOCK IoStatusBlock, -> 0xc2de78
OUT PVOID Buffer, -> 0xc2dea8
IN ULONG BufferLength,-> 0x268
IN FILE_INFORMATION_CLASS FileInformationClass, -> 0x3=FileBothDirectoryInformation
IN BOOLEAN ReturnSingleEntry, -> 1=TRUE
IN PUNICODE_STRING FileName, -> 0xc2de90
IN BOOLEAN RestartScan -> NULL
);
En analysant un peu les arguments, le FileHandle correspond à :
kd> !handle 544
processor number 0, process ffb5f798
PROCESS ffb5f798 SessionId: 0 Cid: 0524 Peb: 7ffd9000 ParentCid: 0504
DirBase: 009a5000 ObjectTable: e1793b28 HandleCount: 340.
Image: explorer.exe
Handle table at e1536000 with 340 Entries in use
0544: Object: 80e39118 GrantedAccess: 00100001 Entry: e1536a88
Object: 80e39118 Type: (80e95e70) File
ObjectHeader: 80e39100
HandleCount: 1 PointerCount: 3
Directory Object: 00000000 Name: \\ {HarddiskVolume1}
Un handle du process explorer.exe de type File, portant le nom de ‘\’ attaché au HarddiskVolume1. En fait c’est tout simplement le handle sur C:\
Le FileName quant à lui contient :
kd> !ustr 00c2de90
String(2,6) at 00c2de90: *
Le fichier demandé est donc C:\*
Enfin le FileInformationClass est mit à FileBothDirectoryInformation (3). Ce qui veut dire que la fonction va renvoyer dans la Buffer en 0xc2dea8 une liste chaînée de structures FILE_BOTH_DIRECTORY_INFORMATION contenant les descriptions des fichiers contenu dans C:\
Vous voyez que c’est pas compliqué Windows :}
Maitenant qu’on sait ce que NtQueryDirectoryFile doit faire, regardons la forme de l’IRP.
kd> !irp ffacb430
Irp is active with 9 stacks 9 is current (= 0xffacb5c0)
No Mdl: No System Buffer: Thread 80d4e020: Irp stack trace.
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
>[ c, 1] 2 0 80e7a408 80e39118 00000000-00000000
FileSystemNtfs
Args: 00000268 ffb03038 00000003 00000000
[ c, 1] Veut dire qu’on à fait appel à une routines IRP_MJ_DIRECTORY_CONTROL(0xc) avec le code IRP_MN_QUERY_DIRECTORY(1). Dans les arguments de l’IRP on peut retrouver la taille du Buffer (0×268) et le FileInformationClass (3). Le Buffer lui est placé dans le champ UserBuffer de l’IRP.
Maintenant qu’on sait tout ca, on peut définir le fonctionnement du hook. Il s’agit de modifier la table des Dispatch routines du driver Nfts afin d’y mettre notre fonction à la place de NtfsFsdDirectoryControl. Notre fonction va vérifier si les arguments passés à la routine vérifient certaines propriétés, sachant qu’on désire modifier le résultat de la requête afin de supprimer dans la liste chainée un ou plusieurs fichiers, il nous faut donc s’intéresser aux requêtes qui vérifient :
- Le champ MinorFunction de l’IRP vaut IRP_MN_QUERY_DIRECTORY
- Le champ Irp->Parameters.QueryDirectory.FileInformationClass vaut une de ces valeurs :
FileDirectoryInformation
FileFullDirectoryInformation
FileBothDirectoryInformation
FileNamesInformation
Si ces conditions ne sont pas vérifiées alors on return en appelant la fonction originale NtfsFsdDirectoryControl pour compléter la requête.
Dans la cas ou les conditions sont OK, on appel toujours la fonction NtfsFsdDirectoryControl puis on parcourt la liste chainée en modifiant au besoins les résultats.
Enfin, il reste aussi la routine NtfsFsdCreate, qui permet à partir d’un nom de fichier de d’obtenir un handle. Ainsi il est possible d’ouvrir un fichier en même si on ne voit pas « directement ». Il faut donc tenir compte de ce problème en posant un hook sur cette routine et en vérifiant le nom du fichier. Si jamais il correspond à ceux qu’on veut cacher alors on renvoie un STATUS_OBJECT_NAME_INVALID.
Dans mon code, tout les fichiers et dossiers commençant par un ‘_’ sont cachés. De plus si l’utiliser essaye d’ouvrir un handle sur le fichier C:\_bouh.txt la routine NtfsFsdCreate renverra STATUS_OBJECT_NAME_INVALID.
Voici de quoi vous amusez
http://ivanlef0u.fr/repo/IRP.rar
Enjoy !
En cas de soucis n’hesitez pas à me poser des questions.
Références :
http://www.rootkit.com/newsread_print.php?newsid=690
http://www.rootkit.com/newsread.php?newsid=647
http://msdn2.microsoft.com/en-us/library/ms795825.aspx
http://msdn2.microsoft.com/en-us/library/ms795806.aspx
http://www.antirootkit.com/articles/Nailuj-Rootkit-Analysis/index.htm
http://ivanlef0u.fr/repo/windoz/hidingfr.txt
avril 26th, 2007
Hiver nucléaire, le ciel s’est assombrit depuis longtemps, les températures ont chuté et le monde s’est retrouvé recouvert de neige. Planète dévastée, population décimée, les derniers survivants s’entassent dans des refuges, luttant chaque jour pour survivre contre le froid et la faim. La routine se résume aux actes les plus basiques, l’inertie environnementale est telle que l’espoir d’une tentative pour améliorer la situation est faible. Depuis le bombardement nucléaire les individus se sont retrouvés disloqués, éparpillés dans des zones de survies, au fur et à mesure se sont crée des nouvelles nations, se bâtissant sur les ruines des anciennes, certes moins puissantes que les originelles elles n’en sont pas moins restées autant autarciques. L’objectif de ces nouvelles patries est simple, le retour de lumière solaire mais uniquement au dessus de leurs territoires, les autres peuvent mourir. Ainsi est née une guerre après la guerre, tout les coups sont permis pour s’approprier les dernières découvertes du voisin.
Pourtant certains s’interrogent sur cette situation, ils envisagent une alliance mondiale visant à rassembler les nations. On leurs rétorque que des ennemis resteront toujours des ennemis et qu’après le retour du soleil ces derniers n’hésiteront pas à briser l’alliance. Au final, chacun travaille de son coté sur le même grand projet et on est en droit de se demander combien de temps l’humanité va t’elle encore souffrir de la vanité.
C’est durant ce contexte que se sont développés des réseaux parallèles d’informations, les idées sont échangées librement entre les membres, sans se partage les plus grandes découvertes n’auraient jamais eu lieu. Cette communauté active, travaille dans l’ombre, désintéressée du profit, son but est pourtant le même que tous mais pour eux un partage global des connaissances est la clé de la réussite.
Pour ceux qui n’auraient pas vu que cette petite histoire écrite sous trip chocapics est une analogie à la manière dont je perçois le monde de l’informatique. D’un coté les grandes sociétés, se croyant maître dans leurs domaine et gardant jalousement leurs découvertes. En face une communauté prônant un partage de l’information, sa puissance provenant de son enrichissement par des idées innovatrices venues de toutes parts. Force est de constater que de nombreuses idées de l’underground informatique on été reprises par les sociétés, surtout dans le domaine de la sécu-info, les hackers sont les inventeurs.
Ainsi en y pensant un peu, j’ai remarqué que c’est grâce à ces hackers que j’ai apprit, uniquement parce que quelques personnes ont jugé qu’il était plus judicieux de partager leurs idées et connaissances plutôt que de les vendre au plus offrant. C’est en fait le but de ce blog, je veux juste partager ce que je sais faire en espérant que grâce à cela d’autres découvrent et apprennent.
Ha je me rends compte que je vous casse les couilles avec mes conneries racontées en caleçon (vous voulez des photos ? envoyez la monnaie ! Et si vous êtes une fille envoyez votre photo d’abord :]). Je me souviens en fait que j’ai mit un titre et qu’il serait plus intéressant que j’essaye de le suivre plutôt que lassé par mes bêtises vous retourniez coder devant votre porno.
#include <pr0n.h>
Ca me revient, j’avais envie de faire un post sur l’IRP hooking, technique de détournement de fonctions qui va se nicher dans le trou du cul du système et qui permet de se passer (dans certains cas) de Hook SSDT et de DKOM. Pour commencer on va regarder un peu comment l’OS gère la communication avec ses drivers. Partons avec le code suivant :
#include <ntddk.h>
#define DeviceName L"\Device\IRPdev"
#define LnkDeviceName L"\Global??\IRPdev" // <=> DosDevicesIRPdev
NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING usLnkName;
RtlInitUnicodeString(&usLnkName,LnkDeviceName);
IoDeleteSymbolicLink(&usLnkName);
IoDeleteDevice(DriverObject->DeviceObject);
DbgPrint("Bye Mofo !!n");
return STATUS_SUCCESS;
}
NTSTATUS DriverDispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
DbgPrint("DriverDispatchCreate reachedn");
__asm{int 3} //BP
Irp->IoStatus.Status=STATUS_SUCCESS;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return Irp->IoStatus.Status;
}
NTSTATUS DriverDispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
DbgPrint("DriverDispatchClose reachedn");
__asm{int 3} //BP
Irp->IoStatus.Status=STATUS_SUCCESS;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return Irp->IoStatus.Status;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
ULONG i,NtStatus;
PDEVICE_OBJECT pDeviceObject=NULL;
UNICODE_STRING usDriverName,usLnkName;
DbgPrint("Hello from KernelLand mastern");
pDriverObject->MajorFunction[IRP_MJ_CREATE]=DriverDispatchCreate;
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=DriverDispatchClose;
RtlInitUnicodeString(&usDriverName,DeviceName);
RtlInitUnicodeString(&usLnkName,LnkDeviceName);
NtStatus=IoCreateDevice(pDriverObject,
0,
&usDriverName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&pDeviceObject);
if(NtStatus!=STATUS_SUCCESS)
{
DbgPrint("Error with IoCreateDevice : 0x%x", NtStatus);
return STATUS_UNSUCCESSFUL;
}
pDeviceObject->Flags|=DO_DIRECT_IO;
NtStatus=IoCreateSymbolicLink(&usLnkName,&usDriverName);
if(NtStatus!=STATUS_SUCCESS)
{
DbgPrint("Error with IoCreateSymbolicLink : 0x%x", NtStatus);
return STATUS_UNSUCCESSFUL;
}
pDriverObject->DriverUnload=DriverUnload;
return STATUS_SUCCESS;
}
Ce code initialise un device crée par IoCreateDevice qui est en fait interface permettant au système de communiquer avec le driver. Les 2 routines DriverDispatch permettant de gérer les requètes. Afin que le process puissent eux aussi communiquer avec le driver on doit créer un Symbolic Link avec la fonction IoCreateSymbolicLink qui place un objet dans l’ObpRootDirectoryObject (\\Global?? ou \\Dosdevice) servant à retrouver le device objet.
Le driver est représenté par un objet DRIVER_OBJECT et le device par un objet (attention!) DEVICE_OBJECT nommé \\Device\\IRPdev.
Pour mieux comprendre le rôle des devices, le driver i8042prt gère à la fois le port souris et le clavier (ps2) ainsi en regardant sous le KD ses caractéristiques on peut voir :
kd> !drvobj driveri8042prt
Driver object (81dac788) is for:
Driveri8042prt
Driver Extension List: (id , addr)
Device Object list:
81d9a7d0 81e0c628
Ce drivers possède 2 devices attachés. Celui ci représente l’interface pour la souris.
kd> !devobj 81d9a7d0
Device object (81d9a7d0) is for:
Driveri8042prt DriverObject 81dac788
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt 81d9a888 DevObjExt 81d9ab18
ExtensionFlags (0000000000)
AttachedDevice (Upper) 81d98040 DriverMouclass
AttachedTo (Lower) 81fe6950 DriverACPI
Device queue is not busy.
Et le dernier concerne l’interface pour le clavier.
kd> !devobj 81e0c628
Device object (81e0c628) is for:
Driveri8042prt DriverObject 81dac788
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt 81e0c6e0 DevObjExt 81e0c970
ExtensionFlags (0000000000)
AttachedDevice (Upper) 81d9d228 DriverKbdclass
AttachedTo (Lower) 81fe6a78 DriverACPI
Device queue is not busy.
Maintenant voici un petit programme qui va communiquer avec le driver vu plus haut :
#include <windows.h>
#include <stdio.h>
ULONG main()
{
HANDLE hFile;
hFile=CreateFile(TEXT("\.\IRPdev"), // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile==INVALID_HANDLE_VALUE)
{
printf("Error with CreateFile : %d n", GetLastError());
return 0;
}
CloseHandle(hFile);
return 0;
}
La fonction CreateFile va demander l’obtention d’un handle, cette action sera gérée par la routine du driver DriverDispatchCreate, ensuite le CloseHande demande la fermeture du handle et sera géré par DriverDispatchClose. Le mieux étant évidemment de tester et de voir par nous même. Après avoir charger le driver dans ma VM et lancer mon programme j’atteint le 1er breakpoint (int 3), en jetant un coup d’œil sur la call stack on observe ceci :
Hello from KernelLand master
DriverDispatchCreate reached
Break instruction exception - code 80000003 (first chance)
irp!DriverDispatchCreate+0x11:
fd2cd4cb cc int 3
kd> kb
ChildEBP RetAddr Args to Child
f95a6a4c 804e3d77 80df6998 80d856a0 80d856a0 irp!DriverDispatchCreate+0x11 [c:driversirpio.c @ 31]
f95a6a5c 80570f9c 80df6980 ffaed7ec f95a6c04 nt!IopfCallDriver+0x31
f95a6b3c 8056386c 80df6998 00000000 ffaed748 nt!IopParseDevice+0xa58
f95a6bc4 80567c63 00000000 f95a6c04 00000040 nt!ObpLookupObjectName+0x56a
f95a6c18 80571477 00000000 00000000 0fffff01 nt!ObOpenObjectByName+0xeb
f95a6c94 80571546 0012ff3c 80100080 0012fedc nt!IopCreateFile+0x407
f95a6cf0 8057167c 0012ff3c 80100080 0012fedc nt!IoCreateFile+0x8e
f95a6d30 804df06b 0012ff3c 80100080 0012fedc nt!NtCreateFile+0x30
f95a6d30 7c91eb94 0012ff3c 80100080 0012fedc nt!KiFastCallEntry+0xf8
0012fe98 7c91d68e 7c810b2c 0012ff3c 80100080 ntdll!KiFastSystemCallRet
0012fe9c 7c810b2c 0012ff3c 80100080 0012fedc ntdll!NtCreateFile+0xc
0012ff34 7c801a4f 00000000 80000000 00000001 kernel32!CreateFileW+0x35f
0012ff58 004006f1 004002e8 80000000 00000001 kernel32!CreateFileA+0x30
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff80 00400807 00000001 00410b20 00410aa0 test+0x6f1
0012ffc0 7c816d4f 00370031 002d0031 7ffdc000 test+0x807
0012fff0 00000000 00400753 00000000 78746341 kernel32!BaseProcessStart+0x23
L’API CreateFile appel l’api native NtCreateFile. Arrivé dans le noyau le système vérifie avec la fonction ObpLookupObjectName que l’objet existe dans l’ObpRootDirectoryObject est plus précisément dans la partie contenant symbolic links, \\Global??. Après avoir récupéré le device object qui contient un pointeur sur le driver object l’I/O Manager forge un IRP qui sera envoyé au driver par l’appel IopfCallDriver. L’IRP (I/O Request Packet) en fait la structure servant de protocole de communication avec les drivers. Enfin notre driver traite la requête en disant quelle à réussi et renvoie le résultat avec IoCompleteRequest.
En relançant l’OS on atteint le second breakpoint et on retrouve le même schéma de fonctionnement.
kd> g
DriverDispatchClose reached
Break instruction exception - code 80000003 (first chance)
irp!DriverDispatchClose+0x11:
fd2cd519 cc int 3
kd> kb
ChildEBP RetAddr Args to Child
f95a6c5c 804e3d77 80df6998 80d856a0 80d856a0 irp!DriverDispatchClose+0x11 [c:driversirpio.c @ 42]
f95a6c6c 8056afec 80cefac8 00000000 00000000 nt!IopfCallDriver+0x31
f95a6ca4 80563ff6 00cefae0 80cefac8 00000000 nt!IopDeleteFile+0x132
f95a6cc0 804e3c55 80cefae0 00000000 0000001c nt!ObpRemoveObjectRoutine+0xdf
f95a6ce4 80567543 80cd2da0 e15da928 80cd7440 nt!ObfDereferenceObject+0x5f
f95a6cfc 805675ac e15da928 80cefae0 0000001c nt!ObpCloseHandleTableEntry+0x155
f95a6d44 805675f6 0000001c 00000001 00000000 nt!ObpCloseHandle+0x87
f95a6d58 804df06b 0000001c 0012ff70 7c91eb94 nt!NtClose+0x1d
f95a6d58 7c91eb94 0000001c 0012ff70 7c91eb94 nt!KiFastCallEntry+0xf8
0012ff60 7c91d592 7c809bbb 0000001c 0012ff80 ntdll!KiFastSystemCallRet
0012ff64 7c809bbb 0000001c 0012ff80 0040071c ntdll!ZwClose+0xc
0012ff70 0040071c 0000001c 0000001c 0012ffc0 kernel32!CloseHandle+0x51
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff80 00400807 00000001 00410b20 00410aa0 test+0x71c
0012ffc0 7c816d4f 00370031 002d0031 7ffdc000 test+0x807
0012fff0 00000000 00400753 00000000 78746341 kernel32!BaseProcessStart+0x23
La fonction !devobj du KD permet de retrouver diverses info sur notre device.
kd> !devobj deviceIRPdev
Device object (80df6998) is for:
IRPdev Driverirp DriverObject 80d884e0
Current Irp 00000000 RefCount 1 Type 00000022 Flags 00000050
Dacl e1259364 DevExt 00000000 DevObjExt 80df6a50
ExtensionFlags (0000000000)
Device queue is not busy.
Cette fonction agit tout simplement en récupérant les infos de la structure DEVICE_OBJECT.
kd> dt nt!_DEVICE_OBJECT 80df6998
+0x000 Type : 3
+0x002 Size : 0xb8
+0x004 ReferenceCount : 1
+0x008 DriverObject : 0x80d884e0 _DRIVER_OBJECT
+0x00c NextDevice : (null)
+0x010 AttachedDevice : (null)
+0x014 CurrentIrp : (null)
+0x018 Timer : (null)
+0x01c Flags : 0x50
+0x020 Characteristics : 0x100
+0x024 Vpb : (null)
+0x028 DeviceExtension : (null)
+0x02c DeviceType : 0x22
+0x030 StackSize : 1 ''
+0x034 Queue : __unnamed
+0x05c AlignmentRequirement : 0
+0x060 DeviceQueue : _KDEVICE_QUEUE
+0x074 Dpc : _KDPC
+0x094 ActiveThreadCount : 0
+0x098 SecurityDescriptor : 0xe1259350
+0x09c DeviceLock : _KEVENT
+0x0ac SectorSize : 0
+0x0ae Spare1 : 0
+0x0b0 DeviceObjectExtension : 0x80df6a50 _DEVOBJ_EXTENSION
+0x0b4 Reserved : (null)
Pareil, la fonction !drvobj permet de voir ce qui notre driver à dans le ventre.
kd> !drvobj Driverirp 3
Driver object (80d884e0) is for:
Driverirp
Driver Extension List: (id , addr)
Device Object list:
80df6998
DriverEntry: fd3145c2 irp!DriverEntry
DriverStartIo: 00000000
DriverUnload: fd31443a irp!DriverUnload
Dispatch routines:
[00] IRP_MJ_CREATE fd31449c irp!DriverDispatchCreate
[01] IRP_MJ_CREATE_NAMED_PIPE 805025e4 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE fd3144ea irp!DriverDispatchClose
[03] IRP_MJ_READ 805025e4 nt!IopInvalidDeviceRequest
[04] IRP_MJ_WRITE 805025e4 nt!IopInvalidDeviceRequest
[05] IRP_MJ_QUERY_INFORMATION 805025e4 nt!IopInvalidDeviceRequest
[06] IRP_MJ_SET_INFORMATION 805025e4 nt!IopInvalidDeviceRequest
[07] IRP_MJ_QUERY_EA 805025e4 nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA 805025e4 nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS 805025e4 nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION 805025e4 nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION 805025e4 nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN 805025e4 nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP 805025e4 nt!IopInvalidDeviceRequest
[13] IRP_MJ_CREATE_MAILSLOT 805025e4 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY 805025e4 nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY 805025e4 nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER 805025e4 nt!IopInvalidDeviceRequest
[17] IRP_MJ_SYSTEM_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[18] IRP_MJ_DEVICE_CHANGE 805025e4 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA 805025e4 nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA 805025e4 nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP 805025e4 nt!IopInvalidDeviceRequest
Qui est au final un parsing des infos contenues dans la structure DRIVER_OBJECT.
kd> dt nt!_DRIVER_OBJECT 0x80d884e0
+0x000 Type : 4
+0x002 Size : 168
+0x004 DeviceObject : 0x80df6998 _DEVICE_OBJECT
+0x008 Flags : 0x12
+0x00c DriverStart : 0xfd314000
+0x010 DriverSize : 0x880
+0x014 DriverSection : 0x80d18928
+0x018 DriverExtension : 0x80d88588 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING "Driverirp"
+0x024 HardwareDatabase : 0x8068de90 _UNICODE_STRING "REGISTRYMACHINEHARDWAREDESCRIPTIONSYSTEM"
+0x028 FastIoDispatch : (null)
+0x02c DriverInit : 0xfd3145c2 irp!DriverEntry+0
+0x030 DriverStartIo : (null)
+0x034 DriverUnload : 0xfd31443a irp!DriverUnload+0
+0x038 MajorFunction : [28] 0xfd31449c irp!DriverDispatchCreate+0
Dans les Drivers Routines on retrouve mes 2 fonctions DriverDispatchCreate et DriverDispatchClose correspondant à des requêtes de type IRP_MJ_CREATE et IRP_MJ_CLOSE. Les autres IRP_MJ_XX sont renvoyés vers une routine de l’I/O Manager, IopInvalidDeviceRequest, servant juste à dire qu’elles n’ont pas abouti.
C’est sur ces routines que repose l’art de l’IRP Hooking, en effet on retrouve une table contenant des pointeurs sur des fonctions chargées de gérer des demandes. Ainsi en modifiant ces pointeurs on peut remplacer ces fonctions par les nôtres et modifier le comportement du driver. Maintenant que le driver de test nous à permit de voir comment cela fonctionnait, on va tenter d’appliquer cette méhtode au driver du système de fichier, ntfs, dans le but, bien évidemment, de faire disparaître des fichiers ! (HAN le méchant !!)
Voici ce que la fonction !drvobj nous sort à propos du driver ntfs
kd> !drvobj Filesystemntfs 3
Driver object (80e8c578) is for:
FileSystemNtfs
Driver Extension List: (id , addr)
Device Object list:
80e7a408 80e8c460
DriverEntry: fcb9c204 Ntfs!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: 00000000
Dispatch routines:
[00] IRP_MJ_CREATE fcb3ce37 Ntfs!NtfsFsdCreate
[01] IRP_MJ_CREATE_NAMED_PIPE 805025e4 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE fcb3c320 Ntfs!NtfsFsdClose
[03] IRP_MJ_READ fcb19ee4 Ntfs!NtfsFsdRead
[04] IRP_MJ_WRITE fcb18bca Ntfs!NtfsFsdWrite
[05] IRP_MJ_QUERY_INFORMATION fcb3d4d1 Ntfs!NtfsFsdDispatchWait
[06] IRP_MJ_SET_INFORMATION fcb1aa58 Ntfs!NtfsFsdSetInformation
[07] IRP_MJ_QUERY_EA fcb3d4d1 Ntfs!NtfsFsdDispatchWait
[08] IRP_MJ_SET_EA fcb3d4d1 Ntfs!NtfsFsdDispatchWait
[09] IRP_MJ_FLUSH_BUFFERS fcb42a68 Ntfs!NtfsFsdFlushBuffers
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fcb3d61c Ntfs!NtfsFsdDispatch
[0b] IRP_MJ_SET_VOLUME_INFORMATION fcb3d61c Ntfs!NtfsFsdDispatch
[0c] IRP_MJ_DIRECTORY_CONTROL fcb3f2c3 Ntfs!NtfsFsdDirectoryControl
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fcb446d5 Ntfs!NtfsFsdFileSystemControl
[0e] IRP_MJ_DEVICE_CONTROL fcb3d61c Ntfs!NtfsFsdDispatch
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN fcb2b621 Ntfs!NtfsFsdShutdown
[11] IRP_MJ_LOCK_CONTROL fcb90b11 Ntfs!NtfsFsdLockControl
[12] IRP_MJ_CLEANUP fcb3ccee Ntfs!NtfsFsdCleanup
[13] IRP_MJ_CREATE_MAILSLOT 805025e4 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY fcb3d61c Ntfs!NtfsFsdDispatch
[15] IRP_MJ_SET_SECURITY fcb3d61c Ntfs!NtfsFsdDispatch
[16] IRP_MJ_POWER 805025e4 nt!IopInvalidDeviceRequest
[17] IRP_MJ_SYSTEM_CONTROL 805025e4 nt!IopInvalidDeviceRequest
[18] IRP_MJ_DEVICE_CHANGE 805025e4 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA fcb3d4d1 Ntfs!NtfsFsdDispatchWait
[1a] IRP_MJ_SET_QUOTA fcb3d4d1 Ntfs!NtfsFsdDispatchWait
[1b] IRP_MJ_PNP fcb5bf3f Ntfs!NtfsFsdPnp
Fast I/O routines:
FastIoCheckIfPossible fcb535ac Ntfs!NtfsFastIoCheckIfPossible
FastIoRead fcb37b85 Ntfs!NtfsCopyReadA
FastIoWrite fcb43097 Ntfs!NtfsCopyWriteA
FastIoQueryBasicInfo fcb3c21a Ntfs!NtfsFastQueryBasicInfo
FastIoQueryStandardInfo fcb3c0ae Ntfs!NtfsFastQueryStdInfo
FastIoLock fcb43a4d Ntfs!NtfsFastLock
FastIoUnlockSingle fcb43b53 Ntfs!NtfsFastUnlockSingle
FastIoUnlockAll fcb9071c Ntfs!NtfsFastUnlockAll
FastIoUnlockAllByKey fcb90861 Ntfs!NtfsFastUnlockAllByKey
AcquireFileForNtCreateSection fcb378ba Ntfs!NtfsAcquireForCreateSection
ReleaseFileForNtCreateSection fcb37901 Ntfs!NtfsReleaseForCreateSection
FastIoQueryNetworkOpenInfo fcb7ee89 Ntfs!NtfsFastQueryNetworkOpenInfo
AcquireForModWrite fcb43855 Ntfs!NtfsAcquireFileForModWrite
MdlRead fcb7ef9d Ntfs!NtfsMdlReadA
MdlReadComplete 8052bb18 nt!FsRtlMdlReadCompleteDev
PrepareMdlWrite fcb7f317 Ntfs!NtfsPrepareMdlWriteA
MdlWriteComplete 80611143 nt!FsRtlMdlWriteCompleteDev
FastIoQueryOpen fcb3bee8 Ntfs!NtfsNetworkOpenCreate
AcquireForCcFlush fcb37762 Ntfs!NtfsAcquireFileForCcFlush
ReleaseForCcFlush fcb37788 Ntfs!NtfsReleaseFileForCcFlush
Nous on s’intéresse à la fonction NtfsFsdCreate chargée de renvoyé un handle sur un fichier. Hop rien de valant une jolie call stack pour comprendre comment sa marche (sachant un bol de chocapics n’ayant qu’un effet limité sur l’augmentation du QI), on se pose un petit BP sur la fonction. Après lancer le notepad je tombe sur ceci :
Breakpoint 0 hit
Ntfs!NtfsFsdCreate:
fcb3ce37 68ac000000 push 0ACh
kd> kb
ChildEBP RetAddr Args to Child
f9a18a04 804e3d77 80e7a408 80d362b8 ffbc69b0 Ntfs!NtfsFsdCreate
f9a18a14 fcbc0876 80d362c8 80e8cf38 ffbc69b0 nt!IopfCallDriver+0x31
f9a18a60 804e3d77 80e7ae88 00000001 80d362b8 sr!SrCreate+0x150
f9a18a70 80570f9c 80e47890 ffaed7ec f9a18c18 nt!IopfCallDriver+0x31
f9a18b50 8056386c 80e478a8 00000000 ffaed748 nt!IopParseDevice+0xa58
f9a18bd8 80567c63 00000000 f9a18c18 00000040 nt!ObpLookupObjectName+0x56a
f9a18c2c 80571477 00000000 00000000 00000101 nt!ObOpenObjectByName+0xeb
f9a18ca8 80571546 0007d938 001000a1 0007d7e4 nt!IopCreateFile+0x407
f9a18d04 8057160e 0007d938 001000a1 0007d7e4 nt!IoCreateFile+0x8e
f9a18d44 804df06b 0007d938 001000a1 0007d7e4 nt!NtOpenFile+0x27
f9a18d44 7c91eb94 0007d938 001000a1 0007d7e4 nt!KiFastCallEntry+0xf8
0007d568 7c91dd09 7c818c14 0007d938 001000a1 ntdll!KiFastSystemCallRet
0007d56c 7c818c14 0007d938 001000a1 0007d7e4 ntdll!NtOpenFile+0xc
0007dfb0 7c80235e 00000000 0014329c 00141004 kernel32!CreateProcessInternalW+0x892
0007dfe8 7ca1def4 0014329c 00141004 00000000 kernel32!CreateProcessW+0x2c
0007ea6c 7ca1dd5e 00010080 00000000 001436ac SHELL32!_SHCreateProcess+0x387
0007eac0 7ca1dc95 0013fdb0 0007eae0 7ca1d797 SHELL32!CShellExecute::_DoExecCommand+0xb4
0007eacc 7ca1d797 00000000 000f8560 0013fdb0 SHELL32!CShellExecute::_TryInvokeApplication+0x49
0007eae0 7ca1d6c9 000f8560 000f8560 0007eb20 SHELL32!CShellExecute::ExecuteNormal+0xb1
0007eaf4 7ca1d665 0007eb20 00105520 000f8560 SHELL32!ShellExecuteNormal+0x30
La fonction CreateProcess afin de lire le binaire à besoin d’un handle. Pour cela l’API NtOpenFile réalise l’appel système qui va faire la demande au driver du chier ntfs. A noter que le module portant le nom « sr » correspond au driver de restauration du système dont le device va se mettre avant le device associé au système de fichier afin de pourvoir lire les requêtes et créer le point de restauration. On peut le voir sous le KD :
kd> !drvobj filesystemntfs
Driver object (80e8c578) is for:
FileSystemNtfs
Driver Extension List: (id , addr)
Device Object list:
80e7a408 80e8c460
kd> !devobj 80e7a408
Device object (80e7a408) is for:
FileSystemNtfs DriverObject 80e8c578
Current Irp 00000000 RefCount 0 Type 00000008 Flags 00000000
DevExt 80e7a4c0 DevObjExt 80e7ac68
ExtensionFlags (0000000000)
AttachedDevice (Upper) 80e7add0 FileSystemsr <----device associé au driver de restauration
Device queue is not busy.
L’architecture du noyau correspond à ce schéma, un série des drivers ayant au dessus d’eux un ou plusieurs devices, provenant soit de leur propre driver, soit associé à d’autres. On a donc une device stack au dessus de chaque driver et l’IRP va devoir traverser cette pile pour arrivé au driver final. On peut le voir avec la commande !devstack
kd> !devstack 80e7a408
!DevObj !DrvObj !DevExt ObjectName
80e7add0 FileSystemsr 80e7ae88
> 80e7a408 FileSystemNtfs 80e7a4c0
Justement en parlant d’IRP, sachant que les XxxDispatchCreate routines sont toujours de la forme suivante :
NTSTATUS
XxxDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
On peut retrouve notre IRP qui correspond au second argument de la fonction NtfsFsdCreate, regardons cela avec notre KD :
kd> dt nt!_IRP 80d362b8
+0x000 Type : 6
+0x002 Size : 0x1b4
+0x004 MdlAddress : (null)
+0x008 Flags : 0x884
+0x00c AssociatedIrp : __unnamed
+0x010 ThreadListEntry : _LIST_ENTRY [ 0xffb68230 - 0xffb68230 ]
+0x018 IoStatus : _IO_STATUS_BLOCK
+0x020 RequestorMode : 1 ''
+0x021 PendingReturned : 0 ''
+0x022 StackCount : 9 ''
+0x023 CurrentLocation : 9 ''
+0x024 Cancel : 0 ''
+0x025 CancelIrql : 0 ''
+0x026 ApcEnvironment : 0 ''
+0x027 AllocationFlags : 0xc ''
+0x028 UserIosb : 0xf9a18b08 _IO_STATUS_BLOCK
+0x02c UserEvent : (null)
+0x030 Overlay : __unnamed
+0x038 CancelRoutine : (null)
+0x03c UserBuffer : (null)
+0x040 Tail : __unnamed
Je vous laisse lire la doc du DDK pour avoir une description de ces différents champs. Par contre au lieu de se faire chier à dumper la structure on va utiliser la commande !irp.
kd> !irp 80d362b8
Irp is active with 9 stacks 9 is current (= 0x80d36448)
No Mdl: No System Buffer: Thread ffb68020: Irp stack trace.
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
>[ 0, 0] 0 0 80e7a408 ffbc69b0 00000000-00000000
FileSystemNtfs
Args: f9a18a9c 01000060 00050000 00000000
Ici notre IRP possède 9 stacks, d’ailleurs j’avoue de ne pas savoir pourquoi, normalement on a une stack par driver accédé, ici on devrait donc en avoir 2 … Bref c’est pas vraiment important pour ce qu’on veut faire, les arguments de l’IRP sont passé par la pile et contenu dans une structure IO_STACK_LOCATION.
kd> dt nt!_IO_STACK_LOCATION 0x80d36448
+0x000 MajorFunction : 0 ''
+0x001 MinorFunction : 0 ''
+0x002 Flags : 0 ''
+0x003 Control : 0 ''
+0x004 Parameters : __unnamed
+0x014 DeviceObject : 0x80e7a408 _DEVICE_OBJECT
+0x018 FileObject : 0x80e402a8 _FILE_OBJECT
+0x01c CompletionRoutine : (null)
+0x020 Context : (null)
Et dans cette magnifique structure se situant au fin fond du système on a un FILE_OBJECT contenant :
kd> !fileobj ffbc69b0
WINDOWSsystem32NOTEPAD.EXE
Device Object: 0x80e478a8 DriverFtdisk
Vpb is NULL
Flags: 0x2
Synchronous IO
CurrentByteOffset: 0
On retrouve notre demande d’ouverture du handle sur le fichier notepad.exe. Vous l’avez compris en hookant les IRP_MJ_xxx du driver ntfs il est possible de renvoyé des valeurs modifiées à l’OS et ainsi par exemple ne plus faire apparaître des fichiers. Je réaliserais un POC prochainement et je vous filerais le code. En attendant vous pouvez retourner devant votre porno :]
PS : Cet articlé ayant été écrit entièrement en caleçon l’auteur s’excuse pour les odeurs.
Références :
Windows DDK -> Handling IRPs
Microsoft Windows Internals, Fourth Edition -> Chapter 9. I/O System
Rootkits: Subverting the Windows Kernel -> Kernel Hooks -> Hooking the Major I/O Request Packet Function Table in the Device Driver Object
avril 21st, 2007
L’histoire de ce post commence dans le train, youpi c’est les vacances mais avant je dois me payer 6h d’ennui à mater les vaches, au lieu de cela je vais plutôt faire un peu de RE. En début de semaine on m’avait fait remarqué que les white papers des confs Blackhat était dispo, l’un d’eux, celui de Joel Eriksson intitulé « Kernel Wars » attira mon attention, il présentait diverses exploitations de vulnérabilités kernel sur des noyaux unix mais aussi et surtout démontrait qu’il était possible d’exploiter la vulnérabilité GDI paru dans le Month Of Kernel Bugs (MOKB). W00T. Mauvais chose pour nous il décrit sans trop de détail comment il à exploité ce bug, fournissant juste une méthodologie et même pas de sploit, OUIN ! Moi après avoir rigoler avec le pauvre BOF de la vuln au niveau des icônes animées (fichiers .ani) je me suis dit que l’écriture d’un sploit pour la vuln GDI ferait un zoli post pour mon blog.
La première dont nous parle Jojo concerne la récupération d’un handle sur un truc appelé la GdiSharedHandleTable. Dans la 1ère version du POC de MOKB, la récupération du handle était fait par brute-force (lil), Jojo lui est plus malin et préfère utiliser des propriétés de l’objet qu’il veut pour obtenir son handle. Sachant qu’un handle correspond juste à l’indice dans la HANDLE_TABLE d’une strucure HANDLE_TABLE_ENTRY, on va regarder cela sous avec notre ami kernel debugger.
Après avoir le code du POC du MKB je note l’indice que me renvoie le brute-force :
HANDLE hMapFile=(HANDLE)0x10;
while(!lpMapAddress){
hMapFile=(void*)((int)hMapFile+1);
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
}
L’objet en question est de type Section, normal car MapViewOfFile (NtMapViewOfSection en fait) ne marche que sur ce type d’objet. Il est donc intéressant de savoir d’ou provient ce handle, hop je lance le POC dans Olly, et je m’aperçois que le handle est déjà présent dans la HANDLE_TABLE alors que je ne suis que sur l’EntryPoint de mon .exe, je décide de dire à Olly de breaker avant le lancement du loader de Windows (Options->Debugging Options->Event, Make first pause at : system breakpoint), je regarde les handles, aucun de type section. C’est donc le loader de win qui l’ajoute dans la table, reste plus qu’à trouver par quelle fonction, hop je trace gentiment en regardant particulièrement les appels au fonction systèmes (seules capable d’ajouter un handle dans la HANDLE_TABLE du process) et je remarque qu’après l’appel à une fonction de GDI32.dll, le nombre de handle est modif et passe de 7 à 11:
Avant l'appel à NtGdiInit :
PROCESS 838b3020 SessionId: 0 Cid: 076c Peb: 7ffde000 ParentCid: 0744
DirBase: 07af6000 ObjectTable: e1f88168 HandleCount: 7.
Image: GDI.exe
Après :
PROCESS 838b3020 SessionId: 0 Cid: 076c Peb: 7ffde000 ParentCid: 0744
DirBase: 07af6000 ObjectTable: e1f88168 HandleCount: 11.
Image: GDI.exe
lkd> !handle 0 3 76C Section
processor number 0, process 0000076c
Searching for Process with Cid == 76c
Searching for handles of type Section
PROCESS 838b3020 SessionId: 0 Cid: 076c Peb: 7ffde000 ParentCid: 0744
DirBase: 07af6000 ObjectTable: e1f88168 HandleCount: 11.
Image: GDI.exe
Notre objet de type section :
Handle table at e109f000 with 11 Entries in use
001c: Object: e1531bf8 GrantedAccess: 000f001f Entry: e109f038
Object: e1531bf8 Type: (843c43b8) Section
ObjectHeader: e1531be0
HandleCount: 19 PointerCount: 20
La call stack :
Thread ID : 1656
0x77ef67eb : GDI32!NtGdiInit+0xc
0x77d1f54e : USER32!_UserClientDllInitialize+0x315
0x7c911193 : ntdll!LdrpCallInitRoutine+0x14
0x7c92c9e4 : ntdll!LdrpRunInitializeRoutines+0x344
0x7c931abc : ntdll!LdrpInitializeProcess+0x1131
0x7c928d66 : ntdll!_LdrpInitialize+0x183
La DLL GDI32 fait appel système sur la fonction NtGdiInit du driver win32k.sys. Hop je regarde ce que NtGdiInit à dans le ventre :
lkd> uf win32k!NtGdiInit
win32k!NtGdiInit:
bf8c1f3c 33c0 xor eax,eax
bf8c1f3e 40 inc eax
bf8c1f3f c3 ret
Grut?? Comme vous le voyez la foncion NtGdiInit n’est pas très consistante, à ce moment un gros doute s’empare de moi, d’ou qu’il sort se put1 de handle FFS. Bon calmons nous et reprenons, l’appel système se réalise de la manière suivante.
NtGdiInit dans GDI32.dll -> KiFastSystemCall -> sysenter | KiFastCallEntry -> KiSystemService -> NtGdiInit
Forcément le handle est ajouté par KiFastCallEntry ou bien KiSystemService, reste plus qu’à regarder.
Dans KiSystemService, je tombe sur le disass suivant :
mov ecx, ds:0FFDFF018h
xor ebx, ebx
or ebx, [ecx+0F70h]
jz short loc_4077C0
push edx
push eax
call ds:_KeGdiFlushUserBatch ; Grut ?
pop eax
pop edx
En fait en 0xFFDFF000 on est dans la structure KPCR (Kernel Process Control Region) dont le premier champ est une structure NT_TIB, et en 0×18 de cette struct on trouve un pointeur sur le TEB du thread courant :
lkd> dt nt!_NT_TIB FFDFF000
+0x000 ExceptionList : 0xf2914c7c _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : 0xf2914df0
+0x008 StackLimit : 0xf2912000
+0x00c SubSystemTib : (null)
+0x010 FiberData : (null)
+0x010 Version : 0
+0x014 ArbitraryUserPointer : (null)
+0x018 Self : 0x7ffde000 _NT_TIB
Et a l’offset 0xF70 du TEP on tombe sur :
dt nt!_TEB
[...]
+0xf70 GdiBatchCount : Uint4B
[...]
Ho le joli compteur, ce que je comprend pour l’instant c’est que la fonction KiSystemService vérifie si le champ GdiBatchCount du TEB du thread courant vaut 0, si oui alors on appel la fonction pointé par la var globale KeGdiFlushUserBatch.
lkd> dd nt!KeGdiFlushUserBatch l1
80561548 bf80db87
lkd> ln bf80db87
(bf80db87) win32k!NtGdiFlushUserBatch | (bf80e078) win32k!BRUSHMEMOBJ::pbrAllocBrush
Exact matches:
win32k!NtGdiFlushUserBatch =
Comme on peut le voir, cette var pointe sur la fonction NtGdiFlushUserBatch de win32k.sys. hop je cours, je saute, je zoom et je fait chauffer IDA sur cette API. Arf grosseuh fonction codé avec un switch à 9 ‘case’ (IDA l’analyse très bien), bon là j’ai pas envie de RE pour savoir quel case du switch est prit en compte, va falloir tracer cela sous VM et puis j’ai plus de batterie :p
De retour chez moi, je lance ma VM en mettant un BP sur NtGdiFlushUserBatch, après 1h de recherche, j’arrive à RIEN ! Apparement le handle ne provient pas de cette fonction …. ….. (TILT!) Je me suis souvenu que eeye avait pondu un advisorie sur cette faille . Tient il parle de la fonction GdiProcessCallout, hop BP dessus et je relance. BIM TOUCHAY!
Voici la call stack :
kd> k
ChildEBP RetAddr
f7f09cd4 bf8465af win32k!GdiProcessCallout+0xb8
f7f09cf0 8057ed06 win32k!W32pProcessCallout+0x5c
f7f09d54 804ded5e nt!PsConvertToGuiThread+0x123
f7f09da0 7c92de0f nt!KiBBTUnexpectedRange+0xc
7ffe0300 7c91eb93 ntdll!LdrDisableThreadCalloutsForDll+0x82
7ffe0308 00000000 ntdll!KiFastSystemCallRet+0x4
Le plus drôle c’est que l’appel à KiBBTUnexpectedRange provient de KiSystemService, il se faisait juste avant celui de NtGdiFlushUserBatch, ouinnn !
Dans GdiProcessCallout je vois un magnifique ObOpenObjectByPointer, qui permet de récupérer un handle sur un objet à partir d’un pointeur.
NTSTATUS
ObOpenObjectByPointer (
__in PVOID Object,
__in ULONG HandleAttributes,
__in_opt PACCESS_STATE PassedAccessState,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode,
__out PHANDLE Handle
)
lea eax, [ebp+SectionHandle] ; Handle renvoyé sur l'objet
push eax
push ebx ; AccessMode = ebx =0
push ebx ; ObjectType
push 0F001Fh ; DesiredAccess : SECTION_ALL_ACCESS
push ebx ; PassedAccessState
push ebx ; HandleAttributes
push _gpHmgrSharedHandleSection ; Object : pointeur sur l'objet
call ds:__imp__ObOpenObjectByPointer@28 ; ObOpenObjectByPointer(x,x,x,x,x,x,x)
Regardons la table des handles de notre process à présent.
kd> !process
PROCESS ffbc86f8 SessionId: 0 Cid: 0418 Peb: 7ffde000 ParentCid: 0530
DirBase: 06673000 ObjectTable: e15c6978 HandleCount: 8.
Image: GDI.exe
VadRoot 80df7d68 Vads 22 Clone 0 Private 27. Modified 0. Locked 0.
DeviceMap e17669a0
Token e1028d48
ElapsedTime 00:00:00.911
UserTime 00:00:00.020
KernelTime 00:00:00.821
QuotaPoolUsage[PagedPool] 10532
QuotaPoolUsage[NonPagedPool] 880
Working Set Sizes (now,min,max) (136, 50, 345) (544KB, 200KB, 1380KB)
PeakWorkingSetSize 136
VirtualSize 8 Mb
PeakVirtualSize 8 Mb
PageFaultCount 132
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 42
THREAD 80de8478 Cid 0418.03f0 Teb: 7ffdd000 Win32Thread: 00000000 RUNNING on processor 0
kd> !handle 0 3 0418 Section
processor number 0, process 00000418
Searching for Process with Cid == 418
Searching for handles of type Section
PROCESS ffbc86f8 SessionId: 0 Cid: 0418 Peb: 7ffde000 ParentCid: 0530
DirBase: 06673000 ObjectTable: e15c6978 HandleCount: 8.
Image: GDI.exe
Handle table at e1043000 with 8 Entries in use
001c: Object: e13ad6b0 GrantedAccess: 000f001f Entry: e1043038
Object: e13ad6b0 Type: (80eb2040) Section
ObjectHeader: e13ad698
HandleCount: 16 PointerCount: 17
Youpi, l’objet apparaît dans la table :]
La fonction ObOpenObjectByPointer est suivie d’un ZwMapViewOfSection qui va mapper dans l’espace mémoire du processus la section.
mov [ebp+BaseAddress], ebx
mov [ebp+ViewSize], ebx
mov [ebp+SectionHandle], ebx
push 2 ; Protect : #define PAGE_READONLY 0x02
push ebx ; AllocationType
push 2 ; InheritDisposition
lea eax, [ebp+ViewSize]
push eax ; ViewSize
push ebx ; SectionOffset
push ebx ; CommitSize
push ebx ; ZeroBits
lea eax, [ebp+BaseAddress]
push eax ; BaseAddress = 0
push 0FFFFFFFFh ; ProcessHandle, -1 = CurrentProcess
push [ebp+SectionHandle] ; SectionHandle
call ds:__imp__ZwMapViewOfSection@40 ; ZwMapViewOfSection(x,x,x,x,x,x,x,x,x,x)
mov edi, eax
cmp edi, ebx
Si l’argument BaseAddress est nul (c’est le cas ici), alors la fonction ZwMapViewOfSection nous donne le premier espace mémoire UserSpace dispo quelle trouve. Après l’appel la fonction renvoie dans [ebp+BaseAddress] 0×410000. La section est donc mappé dans l’espace mémoire du process en lecture seule à l’addresse 0×410000. Jetons un coup d’œil aux VAD.
kd> !vad 80df7d68
VAD level start end commit
80e22728 ( 1) 10 10 1 Private READWRITE
ffbc7ef8 ( 2) 20 20 1 Private READWRITE
80df2648 ( 3) 30 12f 3 Private READWRITE
80da3660 ( 4) 130 132 0 Mapped READONLY
ffaf95b0 ( 5) 140 23f 3 Private READWRITE
ffaeeca8 ( 6) 240 24f 6 Private READWRITE
ffb94308 ( 7) 250 25f 0 Mapped READWRITE
80d25958 ( 8 ) 260 275 0 Mapped READONLY
80d2c1e8 ( 9) 280 2bc 0 Mapped READONLY
80d2c218 (10) 2c0 300 0 Mapped READONLY
80d2c248 (11) 310 315 0 Mapped READONLY
80d25928 (12) 320 3e7 0 Mapped EXECUTE_READ
80d00550 ( 0) 400 406 7 Mapped Exe EXECUTE_WRITECOPY
80cec448 ( 4) 410 512 0 Mapped READONLY <------------ Notre section
80e1f230 ( 3) 77d10 77d9f 2 Mapped Exe EXECUTE_WRITECOPY
80e3a670 ( 4) 77ef0 77f35 1 Mapped Exe EXECUTE_WRITECOPY
80d0c5f0 ( 2) 7c800 7c903 5 Mapped Exe EXECUTE_WRITECOPY
80d00520 ( 1) 7c910 7c9c6 5 Mapped Exe EXECUTE_WRITECOPY
80e71c40 ( 3) 7f6f0 7f7ef 0 Mapped EXECUTE_READ
80d25898 ( 2) 7ffb0 7ffd3 0 Mapped READONLY
80d00900 ( 3) 7ffdd 7ffdd 1 Private READWRITE
80d21228 ( 4) 7ffdf 7ffdf 1 Private READWRITE
Bon calmons nous, on sait maintenant d’ou provient ce handle. Le loader de windows, lors du chargement de user32.dll va appeler la fonction NtGdiInit de GDI32.dll. Durant l’appel système jusqu’à NtGdiInit, le code passe par la fonction KiSystemService (qui se charge en fait de retrouver la fonction dans la SSDT), celle-ci lance KiBBTUnexpectedRange qui à son tour appel W32pProcessCallout à travers un PsConvertToGuiThread. La fonction W32pProcessCallout, à partir d’un pointeur nommé _gpHmgrSharedHandleSection, obtient un handle sur l’objet et le map dans l’espace mémoire du process qui a appelé NtGdiInit… RIGOLO hein! :p :p :p
D’après ce que j’ai vu, Jojo il avait pas remarqué ça et il n’en pas eu besoin pour réaliser son exploit. Ce qui se passe en fait c’est que le handle ouvert par ObOpenObjectByPointer n’est pas refermé, il réside donc toujours dans la table dans handles du process. Si on peut le retrouver, on peu remapper la section en mémoire en écriture car le handle a été ouvert avec tout les droits (SECTION_ALL_ACCESS). C’est pour cela que dans le POC le brute-force fonctionne, le handle sur un objet de type Section se trouvant dans les premiers indices de la table.
Alors voyons d’ou provient l’objet global _gpHmgrSharedHandleSection, 3 coups de IDA et on voit que la section est crée par la fonction HmgCreate, elle même étant appelé par InitializeGre qui est lancée par GsDriverEntry. En explorant le disass de HmgCreate on peut voir :
push ebx
push ebx
push 8400000h ;
push edi
lea eax, [ebp+var_C]
push eax
push ebx
push 6
push offset _gpHmgrSharedHandleSection
mov [ebp+var_8], ebx
mov dword ptr [ebp-0Ch], 102ADCh
call ds:__imp__MmCreateSection@32 ; MmCreateSection(x,x,x,x,x,x,x,x)
test eax, eax
jl loc_BF89083A ;si NULL on se kass
lea eax, [ebp+ViewSize]
push eax ; ViewSize
push offset _gpGdiSharedMemory ; MappedBase
push _gpHmgrSharedHandleSection ; Section
mov [ebp+ViewSize], ebx
call ds:__imp__MmMapViewInSessionSpace@12 ; MmMapViewInSessionSpace(x,x,x)
test eax, eax
jl loc_BF89083A ;si NULL on se kass
mov eax, _gpGdiSharedMemory
cmp eax, ebx
lea ecx, [eax+100000h]
mov _gpentHmgr, eax ; _gpentHmgr=_gpGdiSharedMemory
MmCreateSection crée l’objet _gpHmgrSharedHandleSection puis la fonction MmMapViewInSessionSpace va créer la section dans l’espace kernel. Si on regarde le contenu à l’adresse _gpGdiSharedMemory qui se trouve dans la kernel memory et celui qu’on peut voir dans notre process après le MapViewOfFile, on remarque qu’ils sont identiques. Normal, le MapViewOfFile permet de faire « voir » à la partie userland du process une section de la mémoire noyau.
Maintenant que j’ai mieux compris la chose, je continue de lire le paper de Jojo. Apparemment la section contient une liste de structure GDITableEntry, définie par :
typedef struct
{
DWORD pKernelInfo;
WORD ProcessID;
WORD _nCount;
WORD nUpper;
WORD nType;
DWORD pUserInfo;
} GDITableEntry;
Tout les process ayant cette liste en commun seul le champ ProcessID permet de savoir à quel process appartient une structure. Comme le dit si bien Jojo, c’est en manipulant la valeur pKernelInfo qu’il est possible d’écrire dans le KernelSpace. Il avoue quand même qu’il à chialé sa race pour trouver quelque chose de correct :]
« The methodology used for finding a way to achieve an arbitrary memory overwrite was partially trial and error [...] »
Alors il faut trouvé une fonction système, qu’on peut appeler du userland, qui manipule cette section et en particuler le champ pKernelInfo. Je recherche donc des fonction en Nt**** dans win32k.sys faisant référence à l’espace mémoire pointé par _gpGdiSharedMemory et _gpentHmgr ……
[.............................Traversée du désert .............................]
2 jours de recherches, 2 put1 de jours à tester des centaines de possibilités et à bouffer des chocapics, j’ai faillit craquer plusieurs fois mais j’ai finalement réussi.
Il me fallait trouver un bout de code qui modifiait une adresse définie, je l’ai obtenu (non sans mal) dans la fonction bDeleteBrush du driver win32k.sys. Cette fonction permet de supprimé un objet de type « brush » crée par CreateSolidBrush, la supression se faisant à l’aide de l’API DeleteObject. Après avoir modifié la valeur pKernelInfo de la structure GDITableEntry correspondant à mon « brush » et essayé divers combinaisons, que seule une personne se droguant aux chocapics aurait pu penser, j’ai réussir à écrire à une adresse arbitraire. Le dissass est le suivant :
win32k.sys bDeleteBrush
mov esi, [edx] ;esi=pKernelInfo
cmp [esi+4], ebx ; ebx=0, il faut que [esi+4]>0
mov eax, [edx+0Ch]
mov [ebp+var_8], eax
ja short loc_BF80C1E7 ;jump si [esi+4] > 0
loc_BF80C1E7:
mov eax, [esi+24h] ; [esi+24] = addr qu'on veut fister
mov dword ptr [eax], 2
Ainsi si pKernelInfo pointe sur un buffer crafté par nos soins et qu’en 0×24 de ce buffer se trouve une adresse valide, son contenu prendra la valeur 2.
Alors LOL?! qu’est ce qu’on peut foutre avec ca ?! Hé bien le trick de maladouf consiste à modifier l’adresse d’un appel système (contenu dans la SSDT) pour qu’il soit rediriger vers une adresse basse, c’est-à-dire dans le userspace. Par exemple :
lkd> dps bf998300 L 2
bf998300 bf934921 win32k!NtGdiAbortDoc
bf998304 bf94648d win32k!NtGdiAbortPath
Doit devenir :
lkd> dps bf998300 L 2
bf998300 00000002
bf998304 bf94648d win32k!NtGdiAbortPath
Ainsi si on mappe en userspace à l’adresse 0×2 un payload et qu’on appel la fonction native NtGdiAbortDoc, BIM ! COUP DE TETE, BALAYETTE, MANCHETTE !!!!!! le payload sera exécuté. J’ai choisit la SSDT du driver win32k.sys car celle du ntoskrnl est en lecture seule (merci à Jojo pour toutes ces infos).
Pour allouer de la mémoire en 0×2 il suffit d’utiliser NtAllocateVirtualMemory. Ensuite la SSDT du driver win32k.sys n’étant pas chargée à une adresse constante, j’ai du récup l’imageBase du driver avec un NtQuerySystemInformation ayant l’InformationClass mise à SystemModuleInformation (11). Enfin Pour l’appel à l’API native NtGdiAbortDoc, on réutilise direct le code ASM de l’appel natif contenu dans gdi32.dll :
lkd> uf GDI32!NtGdiAbortDoc
GDI32!NtGdiAbortDoc:
77f3073a b800100000 mov eax,1000h
77f3073f ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
77f30744 ff12 call dword ptr [edx]
77f30746 c20400 ret 4
Concernant le shellcode, j’ai utilisé un payload générique faisait reboot la machine :] (le même que celui de mon post « kernel BOF »). Je me réserve le droit d’en coder un plus pratique pour owner de la b0x :p
Autrement Immunity a achété les droits du sploit jusqu’à la fin du moi d’Avril, ces mofo devait surement croirent que personne n’oserait codé un sploit sur cette faille. C’est désormais chose faite et j’en ai profité pour le release sur milw0rm.
Ivanlef0u, OMG th4t w4s s0 l33t !
le sploit here :
http://ivanlef0u.fr/repo/GDI-MS07-017.rar
Références :
http://www.microsoft.com/technet/security/bulletin/MS07-017.mspx
http://research.eeye.com/html/alerts/zeroday/20061106.html
http://projects.info-pull.com/mokb/MOKB-06-11-2006.html
https://www.blackhat.com/presentations/bh-eu-07/Eriksson-Janmar/Whitepaper/bh-eu-07-eriksson-WP.pdf
http://www.securityfocus.com/bid/20940/info
avril 8th, 2007
Toujours en quête d’idées folles je suis tombé sur un truc funny en lisant le SDK, c’est marrant de se dire que tout le monde utilise cette fonctionnalité pour faire des choses « utiles » alors que moi je la détourne dans le but d’avancer mon projet personnel intitulé « CONQUERIR LE MONDE OU RIEN ! »
Il s’agit juste d’une technique d’injection de code Userland dont le principal intérêt est qu’on ne sait pas vraiment quand notre code sera exécuté :]
Les APC (Asynchronous Procedure Calls) permettent d’interrompre l’exécution d’un thread, si celui-ci l’autorise, pour pouvoir exécuté une routine. La chose est simple, on copie dans l’espace mémoire du process notre routine et on l’a met en file d’attente avec la fonction QueueUserAPC() puis on attend (environ 5 mins au micro-ondes). Pour lancer cette routine le thread doit passer dans un état dit « Alertable », il ne le fait que si le paramètre bAlertable des API suivantes est mit à vrai :
SleepEx,
SignalObjectAndWait,
WaitForSingleObjectEx,
WaitForMultipleObjectsEx,
MsgWaitForMultipleObjectsEx
Clairement notre procédure mise en attente à peu de chance d’être lancer immédiatement, pour avoir plus de chance on la met en file d’attente sur tout les thread du process. Je l’ai testé sur le Notepad et c’est quand je fais Fichier->Ouvrir que ma procédure injectée est lancé, surement à cause de l’appel à GetOpenFileName() qui load le menu pour choisir le fichier à ouvrir.
Après avoir mit un BP sur l’API native ZwWaitForSingleObject dans ntdll et tracer le notepad, j’arrive à la call stack :
Call stack of main thread
Address Stack Procedure / arguments Called from Frame
0007D2B0 77E60ACB ? kernel32.WaitForSingleObjectEx RPCRT4.77E60AC5
0007D2B4 00000131 hObject = 00000131
0007D2B8 000DBBA0 Timeout = 900000. ms
0007D2BC 00000001 fAlertable = TRUE <--W00T
0007D2CC 77E60A81 ? RPCRT4.77E60AA9 RPCRT4.77E60A7C
0007D2F0 77E652C7 RPCRT4.77E60A64 RPCRT4.77E652C2 0007D2EC
0007D318 77E6520D Includes RPCRT4.77E652C7 RPCRT4.77E6520A 0007D314
0007D338 77E6565F RPCRT4.77E60833 RPCRT4.77E6565A 0007D334
0007D440 77E653ED RPCRT4.77E6553D RPCRT4.77E653E8 0007D43C
0007D488 77E64CF6 RPCRT4.77E65372 RPCRT4.77E64CF1 0007D484
0007D4D8 77E64E0D ? RPCRT4.77E64BEB RPCRT4.77E64E08 0007D4D4
0007D51C 77E64D5C ? RPCRT4.77E64D85 RPCRT4.77E64D57 0007D518
0007D580 77E600AC RPCRT4.77E600DC RPCRT4.77E600A7 0007D57C
0007D5B0 77E58DC9 Includes RPCRT4.77E600AC RPCRT4.77E58DC6 0007D5AC
0007D5C8 77E58E00 RPCRT4.I_RpcGetBufferWithObject RPCRT4.77E58DFB 0007D5C4
0007D5D8 77E5942D RPCRT4.I_RpcGetBuffer RPCRT4.77E59428 0007D5D4
0007D5E8 77ED360B ? RPCRT4.NdrGetBuffer RPCRT4.77ED3606 0007D5E4
0007D9C8 77DB1CD0 ? ADVAPI32.77DB1CCB 0007D9C4
0007D9DC 77DB1C52 ADVAPI32.77DB1CB5 ADVAPI32.77DB1C4D 0007D9D8
0007DA30 77DCCB54 ? ADVAPI32.LsaOpenPolicy ADVAPI32.77DCCB4F 0007DA2C
0007DA84 778E8FEB Includes ADVAPI32.77DCCB54 778E8FE9 0007DA80
0007DAA4 778EA005 778EA091 778EA000 0007DAA0
0007DAD4 778E9068 ? 778E9FE8 778E9063 0007DAD0
0007DB18 7CA0F5AE ? 778E902D SHELL32.7CA0F5A8 0007DB14
0007DB98 7CA0F54C SHELL32.7CA0F581 SHELL32.7CA0F547 0007DB94
0007DBB0 7CA0F425 SHELL32.7CA0F4FB SHELL32.7CA0F420 0007DBAC
0007DBD0 7CA0F4F1 SHELL32.7CA0F3D9 SHELL32.7CA0F4EC 0007DBCC
0007DBF8 7C9FA276 SHELL32.7C9FA29D SHELL32.7C9FA271 0007DBF4
0007DC14 7CA00019 SHELL32.7C9FA230 SHELL32.7CA00014 0007DC10
0007DC48 7C9FDF0F SHELL32.7C9FE3E1 SHELL32.7C9FDF0A 0007DC44
0007DCAC 7C9FDE50 Includes SHELL32.7C9FDF0F SHELL32.7C9FDE4D 0007DCA8
0007DD14 7C9FE07B Includes SHELL32.7C9FDE50 SHELL32.7C9FE078 0007DD10
0007DD3C 7C9FE019 SHELL32.7C9FE059 SHELL32.7C9FE014 0007DD38
Là notre APC sera exécuté car le thread va devenir Alertable.
Ha on me dit dans l’oreillette qu’on y comprend rien, éclaircissons tout cela
C:\ProgHack\c>StackWatcher notepad.exe
Thread ID : 2232
0x77e60a64 : RPCRT4!UTIL_GetOverlappedResultEx+0x1d
0x77e65263 : RPCRT4!CO_SyncRecv+0x71
0x77e60833 : RPCRT4!OSF_CCONNECTION::TransSendReceive+0x9d
0x77e6553d : RPCRT4!OSF_CCONNECTION::SendBindPacket+0x575
0x77e65372 : RPCRT4!OSF_CCONNECTION::ActuallyDoBinding+0xa6
0x77e64beb : RPCRT4!OSF_CCONNECTION::OpenConnectionAndBind+0x20f
0x77e64d85 : RPCRT4!OSF_CCALL::BindToServer+0xed
0x77e600dc : RPCRT4!OSF_BINDING_HANDLE::AllocateCCall+0x2b0
0x77e60084 : RPCRT4!OSF_BINDING_HANDLE::NegotiateTransferSyntax+0x28
0x77e58d81 : RPCRT4!I_RpcGetBufferWithObject+0x5b
0x77e58df1 : RPCRT4!I_RpcGetBuffer+0xf
0x77e59405 : RPCRT4!NdrGetBuffer+0x28
0x77ed34d0 : RPCRT4!NdrClientCall2+0x195
0x77db1cb5 : ADVAPI32!LsarOpenPolicy2+0x1b
0x77db1c0f : ADVAPI32!LsaOpenPolicy+0x95
0x77dccaf6 : ADVAPI32!LookupPrivilegeValueW+0x66
0x778ea091 : SETUPAPI!EnablePnPPrivileges+0x2d
0x778e9fe8 : SETUPAPI!PnPGetGlobalHandles+0x1d
0x778e902d : SETUPAPI!CM_Get_Device_Interface_List_Size_ExW+0x45
0x7ca0f581 : SHELL32!CMountPoint::_EnumVolumes+0xc5
0x7ca0f4fb : SHELL32!CMountPoint::_InitLocalDriveHelper+0x52
0x7ca0f3d9 : SHELL32!CMountPoint::_InitLocalDrives+0xc8
0x7c9fa29d : SHELL32!CMountPoint::_GetMountPointDL+0x48
0x7c9fa230 : SHELL32!CMountPoint::GetMountPoint+0x46
0x7c9fe3e1 : SHELL32!CDrivesFolder::_FillIDDrive+0x5c
0x7c9fde70 : SHELL32!CDrivesFolder::ParseDisplayName+0x9f
0x7c9fdddf : SHELL32!CRegFolder::ParseDisplayName+0x93
0x7c9fe059 : SHELL32!CDesktopFolder::_ChildParseDisplayName+0x22
0x7c9fdf96 : SHELL32!CDesktopFolder::ParseDisplayName+0x7e
0x7c9fdddf : SHELL32!CRegFolder::ParseDisplayName+0x93
0x7c9fdd09 : SHELL32!SHParseDisplayName+0xa3
0x7c9fe2f9 : SHELL32!ILCreateFromPathEx+0x3d
0x7ca01b98 : SHELL32!_CreateFolderIDList+0x102
0x7c9ff293 : SHELL32!_GetFolderIDListCached+0x84
0x7c9ff3ed : SHELL32!SHGetFolderLocation+0x9e
0x7c9ff46f : SHELL32!SHGetSpecialFolderLocation+0x17
0x7634545c : comdlg32!CFileOpenBrowser::InitLookIn+0x51
0x763436d1 : comdlg32!InitLocation+0x102
0x763425e4 : comdlg32!OpenDlgProc+0x3af
0x77d1870c : USER32!InternalCallWinProc+0x28
0x77d240d8 : USER32!UserCallDlgProcCheckWow+0x146
0x77d23f5a : USER32!DefDlgProcWorker+0xa8
0x77d1b7d3 : USER32!SendMessageWorker+0x448
0x77d24956 : USER32!InternalCreateDialog+0x9df
0x77d26110 : USER32!InternalDialogBox+0xa9
0x77d261d2 : USER32!DialogBoxIndirectParamAorW+0x37
0x77d32043 : USER32!DialogBoxIndirectParamW+0x1b
0x763433ee : comdlg32!NewGetFileName+0x240
0x763433d6 : comdlg32!NewGetOpenFileName+0xf
0x76343324 : comdlg32!GetFileName+0x35d
0x76357c65 : comdlg32!GetOpenFileNameW+0x62 <---LOL !
0x1002b87 : notepad!NPCommand+0x202
0x1003429 : notepad!NPWndProc+0x4fe
0x77d1870c : USER32!InternalCallWinProc+0x28
0x77d1875f : USER32!UserCallWinProcCheckWow+0x150
0x77d188f1 : USER32!DispatchMessageWorker+0x306
0x77d18a01 : USER32!DispatchMessageW+0xf
0x1002936 : notepad!WinMain+0xdc
0x100739d : notepad!WinMainCRTStartup+0x174
Ha c’est beaucoup mieux, si on regarde dans RPC4CRT.dll à la fonction CO_SyncRecv :
push [ebp+dwMilliseconds] ; dwMilliseconds
lea eax, [ebp+arg_0]
push 1 ; bAlertable <-- W00T :}
push eax ; int
lea eax, [esi+34h]
push eax ; int
push esi ; int
call ?UTIL_GetOverlappedResultEx@@YGJPAXPAU_OVERLAPPED@@PAKHK@Z
Je rappel juste pour ceux dont le cerveau aurait BSOD que c’est la fonction GetOpenFileNameW qui est appelé quand on fait Fichier->Ouvrir :p
Bref une technique marrante qui à l’avantage de ne pas crée de nouveau thread. De plus si le thread visé tourne avec des privilèges supérieur alors votre routine, si elle est lancée, les aura aussi :]
Le code/binaire ici :
http://ivanlef0u.fr/repo/APC.rar
Enjoy !
ps : Ce post n’est pas un toto d’avril :p
avril 1st, 2007