Mysterie's blog

Aller au contenu | Aller au menu | Aller à la recherche

mercredi, juillet 22 2009

A la quête du frame buffer

C'est l'été, dehors il fait beau, les oiseaux chantent. Autant de bonne raison pour resté chez moi et coder.

Toujours à la recherche de mon frame buffer, il existe quelques méthodes pour récupérer l'adresse physique de celui-ci. Mais rien pour l'adresse virtuelle. J'ai eu l'idée d'envoyer des IOCTL au driver miniport de ma carte graph' pour qu'il me renvoi les informations que je recherche. Pour se faire j'utilise la fonction DeviceioControl() qui nécessite un handle. Ce handle on le récupère avec CreateFile("\\\\.\\DISPLAY1, ...); Sauf qu'en retour on a le droit à un beau "Access denied". ouch...

DISPLAY1 est un lien symbolique vers \Device\Video0, chaques devices est lié à un driver, sous VMware on a:

kd> !devobj Video0
Device object (8163e870) is for:
 Video0 \Driver\vmx_svga DriverObject 8164f880

kd> !drvobj vmx_svga 3
Driver object (8164f880) is for:
 \Driver\vmx_svga
Driver Extension List: (id , addr)
(8164f880 816e47f8)  
Device Object list:
8163e870  

DriverEntry:   f9eee240	vmx_svga
DriverStartIo: 00000000	
DriverUnload:  f97aa7e6	VIDEOPRT!VpDriverUnload
AddDevice:     f97ac418	VIDEOPRT!VpAddDevice

Dispatch routines:
[00] IRP_MJ_CREATE                      f97ab65c	VIDEOPRT!pVideoPortDispatch
[01] IRP_MJ_CREATE_NAMED_PIPE           805025e4	nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE                       f97ab65c	VIDEOPRT!pVideoPortDispatch
[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              f97ab65c	VIDEOPRT!pVideoPortDispatch
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     805025e4	nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN                    f97ab65c	VIDEOPRT!pVideoPortDispatch
[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                       f97a7d80	VIDEOPRT!pVideoPortPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL              f979f748	VIDEOPRT!pVideoPortSystemControl
[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                         f97a7150	VIDEOPRT!pVideoPortPnpDispatch


Hop un petit schéma pour les autistes:

   |                  |---------------------->| I/O Manager |
   | GDI (win32k.sys) |  GreDeviceIoControl +>| (Video0)    |    |
 +>|                  | |                   |                    |
 | Eng call             |                   |                    |
 |                      |                   |_| Video Port     |<+
 |             Ddi call |                   +>| (Videoprt.sys) | |
 |_|                  |<+                   |                    |
   | Display driver   |                     |_| Video Miniport |<+
 +>| (vmx_fb.dll)     |                       | (vmx_svga.sys) |<--+
 |          ___________________________________________            |
 |_________|             La carte Graph \o/            |___________|


Grâce a un petit script made in Invanlef0u on va pouvoir retrouver les handles qui nous intéressent. J'ai aussi codé un driver qui dump l'object table du process System.

kd>bp nt!zwopenfile ".printf \"ZwOpenFile \"; !ustr poi(poi(@esp+4+2*4)+2*4);"

// Ca break sur \Device\Video0
kd> !handle 80000478
processor number 0, process 815c3020
PROCESS 815c3020  SessionId: 0  Cid: 025c    Peb: 7ffde000  ParentCid: 0168
    DirBase: 07136000  ObjectTable: e148c9a0  HandleCount:  25.
    Image: csrss.exe

Kernel Handle table at e1539000 with 58 Entries in use
80000478: Object: 816e48e8  GrantedAccess: 00000000 Entry: e10028f0
Object: 816e48e8  Type: (817b8730) File
    ObjectHeader: 816e48d0 (old version)
        HandleCount: 1  PointerCount: 2


L'handle est ouvert avec DesiredAccess = FILE_SUPERSEDED et ShareAccess = 0. Aïe pas moyen d'interagir avec. D'ailleurs quand le device est chargé on se rend compte que le handle a été refermé, il y a juste un pointeur sur l'objet video0. Donc on ne cherche pas dans la bonne direction... :(

Il existe une fonction noyau équivalente à DeviceioControl qui est IoBuildDeviceIoControlRequest (merci Overclok pour l'indication). Elle crée des irp de type IRP_MJ_INTERNAL_DEVICE_CONTROL et IRP_MJ_DEVICE_CONTROL. C'est le deuxième type qui nous intéresse, le driver vidéo n'implante pas la première. Et pour cette fonction nul besoin d'un handle, un pointeur vers l'objet video0 suffit.

Je crée un autre bp sur la fonction IoGetDeviceObjectPointer() pour trouver ce pointeur au boot.

kd>bp nt!IoGetDeviceObjectPointer "!ustr poi(@esp+4); .printf \"\n\tFile:\"; dd poi(@esp+4+4*2) L1; .printf \"\n\tDevice:\"; dd poi(@esp+4+4*3) L1;"
kd>g
String(28,30) at f9c7ac44: \Device\Video0
 	File:f9c7ac68  f9c7ad18
 	Device:f9c7ac84  00000000
nt!IoGetDeviceObjectPointer:
805904b9 8bff            mov     edi,edi
kd>ba w 1 0xf9c7ac84

//Puis ca break le pointeur vers video0 est initialisé.
kd> dt nt!_DEVICE_OBJECT 0x816cac30
   +0x000 Type             : 3
   +0x002 Size             : 0x3a0
   +0x004 ReferenceCount   : 1
   +0x008 DriverObject     : 0x816ce488 _DRIVER_OBJECT
   +0x00c NextDevice       : (null) 
   +0x010 AttachedDevice   : (null) 
   +0x014 CurrentIrp       : (null) 
   +0x018 Timer            : (null) 
   +0x01c Flags            : 0x284c
   +0x020 Characteristics  : 0x100
   +0x024 Vpb              : (null) 
   +0x028 DeviceExtension  : 0x816cace8 
   +0x02c DeviceType       : 0x23
   +0x030 StackSize        : 2 ''
   +0x034 Queue            : __unnamed
   +0x05c AlignmentRequirement : 0
   +0x060 DeviceQueue      : _KDEVICE_QUEUE
   +0x074 Dpc              : _KDPC
   +0x094 ActiveThreadCount : 0
   +0x098 SecurityDescriptor : 0xe14b3680 
   +0x09c DeviceLock       : _KEVENT
   +0x0ac SectorSize       : 0
   +0x0ae Spare1           : 0
   +0x0b0 DeviceObjectExtension : 0x816cafd0 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null) 


Ensuite il suffit d'envoyer un IoControlCode de type IOCTL_VIDEO_MAP_VIDEO_MEMORY avec les structures VIDEO_MEMORY et VIDEO_MEMORY_INFORMATION à zero. Et on obtient les adresses virtuelles de la mémoire vidéo et du frame buffer, dans mon cas:

Video Memory:
0xf86d8000 size: 16777216
Frame buffer:
0xf86d8000 size: 1920000

kd> dd 0xf86d8000
f86d8000  00edf6f5 00ecf5f4 00edf2f5 00ecf1f4
f86d8010  00ecf1f5 00ecf1f5 00edf0f7 00edf0f7
f86d8020  00f1f2f7 00f2f3f7 00f4f5f9 00f6f8f7
f86d8030  00f7f7f5 00f7f8f3 00f5f6f0 00f5f6f0


Voilà les premiers pixels de l'écran. Mais ce qu'il y a de plus intéressant c'est la taille du frame buffer par rapport à la taille de la mémoire vidéo. 16777216-1920000 = 14857216 octet. Donc 14,857Mo dans le kernel space non utilisé où on peut y cacher plein de chose. Étonnant non?

J'utilise ObReferenceObjectByName() fonction non documenté, avec le nom du driver miniport "vmx_svga", pour récupérai le pointeur sur le device video0. D'ailleurs grâce à cette fonction on pourrai lister tout les devices chargés, et leurs envoyé des IOCTL, moi je me suis limité à la vidéo.

Si vous voulez tester le code de votre coté, pour retrouver le nom du driver vidéo il suffit de chercher dans les registres.
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\VIDEO clef \Device\Video0
contient un identifiant chez moi E27CB637-47DA-4B2B-8B0D-50E8CD7B9FAC. On s'en sert ensuite
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video\{E27CB637-47DA-4B2B-8B0D-50E8CD7B9FAC}\Video et la clef Service contient le nom de notre driver miniport. Donc dans mon code il faudra remplacer le nom du driver "vmx_svga" par le votre.
Oui j'ai baclé la fin, j'éditerai surement le post pour automatiser la recherche, ou pas.

Code: framebuf.sys

jeudi, mai 21 2009

D3D Hooking

En se moment je m'intéresse à la programmation Direct3D. J'ai réalisé un programme bidon qui affiche un symbole en 3d et le fait tourner sur lui même. Pas d'anti-aliasing ou autre joyeuseté du style je me suis simplement servi de vertex (une vertex/vertice pourrais se résumer à un point). Le but de ce programme est d'avoir une base pour réaliser ensuite un hook sur la fonction manipulant les vertex et leurs informations histoire de retrouver les couleurs et coordonnées des points du symbole affiché.

Chaque point de la figure est stoqué dans un tableau de CUSTOMVERTEX, la structure CUSTOMVERTEX est défini par la méthode SetFVF() qui se trouve dans l'objet IDirect3DDevice9. Je défini mon point par D3DFVF_XYZ|D3DFVF_DIFFUSE se qui représente { (float)X, (float)Y, (float)Z, (DWORD)color, }

Une autre méthode intéressante est SetStreamSource() faisant partie du même objet, elle va aller toucher au buffer contenant touts les points, et cela à chaque fois qu'on affiche une image. Il suffit donc d'injecter une dll dans le programme et de retrouver IDirect3DDevice9::SetStreamSource() pour la hooker. Sauf que l'objet IDirect3DDevice9 est en mémoire, où? impossible à savoir de l'extérieur.

Le code important dans le programme est:

// (LPDIRECT3DDEVICE9)		gPd3dDevice
// (LPDIRECT3DVERTEXBUFFER9)	gVertexBuff
// Préparation du vertex buffer gPd3dDevice->SetStreamSource(0, gVertexBuff, 0, sizeof(CUSTOMVERTEX)); // Définition de la taille d'un point/vertex gPd3dDevice->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE); // on dessine un premier triangle gPd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 1); // puis le second... et ainsi de suite pour avoir le symbole gPd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 3, 1);


Voici la version assembleur pour l'appel à
gPd3dDevice->SetStreamSource(0, gVertexBuff, 0, sizeof(CUSTOMVERTEX));

mov edx,dword ptr ds:[gVertexBuff] ; on met l'adresse de gVertexBuff dans edx mov eax,dword ptr ds:[gPd3dDevice] ; on met l'adresse de gPd3dDevice dans eax mov ecx,dword ptr ds:[eax] ; ecx pointe vers la table de fonction de l'objet DIRECT3DDEVICE9 push 10 ; Stride (taille d'un point/vertex) 0x10 = 16 = DWORD*4 push 0 ; Offset push edx ; l'adresse de gVertexBuff push 0 ; StreamNumber push eax ; l'adresse de gPd3dDevice mov   eax,dword ptr ds:[ecx+190] ; 64eme fonction de l'objet d3dDevice call  eax ; gPd3dDevice->SetStreamSource(0, edx, 0, 10);


Donc l'objet IDirect3DDevice9 contient une table de fonction, la fonction SetStreamSource est la 64eme et fait référence au module d3d9.dll. En cherchant un peu je retrouve comment est initialisé l'objet. En fait il se base sur une autre table de fonction qui se situe aussi dans le module d3d9.dll et dont SetStreamSource est la 134eme fonction. Comme je ne peux pas savoir comment est structuré l'objet en mémoire je me baserai sur la table de fonction de d3d9.dll pour retrouver SetStreamSource.

La table se situe dans la section .text à l'offset 0x15C38, C'est hardcodé donc c'est moche et ça ne marchera que pour certaine version de la librairie. mais je suis obliger de procéder de cette manière car SetStreamSource n'est pas exporté. D'ailleurs la plupart des codes que j'ai trouvé utilisent des offset.
Sinon il y a plusieurs alternatives, dont le hook de l'IAT sur des fonctions de type d3d8thk.* qui sont d'anciennes fonctions de d3d8.dll utilisés dans la version neuf par souci de compatibilité. ou encore hooker la fonction Direct3DCreate9 qui elle est exporté.

Je préfère procéder par un hook inline, sodomite inside. Donc la DLL que je vais injecté vas retrouver SetStreamSource(), la rediriger vers une fonction intermédiaire qui aura pour but de récupérer les arguments de la pile. Comme la DLL injecté fait partie d'un nouveau thread je vais faire un Sleep(100). Les autres thread du programme vont avoir le temps de s'exécuter et d'utiliser ma fonction hooké et c'est gagné. Après le Sleep() j'aurai accès au argument de SetStreamSource().

On aura donc l'adresse des objets IDirect3DDevice9, IDirect3DVertexBuffer9 ainsi qu'a la taille du CUSTOMVERTEX. Ensuite on utilise les objets comme s'ils nous appartenaient. Donc pour avoir accès aux fameux points de la figure il suffit de faire un:

// pVertices contiendra l'adresse du début de la mémoire ou sont stoqué les vertex
// gVertexBuff représente l'objet IDirect3DVertexBuffer9
gVertexBuff->Lock(0, vertexSize, (void **)&pVertices, 0);
gVertexBuff->Unlock();


Ensuite on peut manipuler ces objets à volonté, lire/changer la position des points, la couleur etc... Le code est disponible mais ça reste un PoC, je change juste la couleur du symbole rien d'extraordinaire. J'ai surtout fait ça pour cheater à l'iut :] For Aiur!

Version de d3d9: 5.3.2600.2180
Le programme: d3d.exe - source
La dll à injecter: d3d.dll - source
Le screen: ici