KdSystemDebugControl

mars 30th, 2010 at 06:13 admin

Je commence enfin à jouer avec Windows 7. Depuis le temps que je m’amusais avec mon vieux XP il fallait bien que cela change. Justement arrivé sur Win7 j’ai voulu manipuler NtSystemDebugControl l’API qui permet de faire tout ce qu’on veut depuis l’user-land sans se fatiguer avec un driver ou \Device\PhysicalMemory. Sauf qu’avec Win7 une bonne partie des features cools ne sont plus disponibles. Embêtant lorsqu’on veut bidouiller son OS, surtout si on est sur la version 64bits ou un driver signé est obligatoire. Justement en bootant en mode DEBUG, Windbg des Debugging Tools for Windows est capable de faire des choses sympa en Local Kernel Debugging. Justement sachant qu’il fonctionne en user-land, par où passe-t-il pour récupérer des informations sur la mémoire noyau ou sur les MSRs ? Avant il passait par NtSystemDebugControl mais sous Win7 ?

En fait rien de nouveau, Alex Ionescu avait déjà parlé de ce mécanisme à Recon 2k6 dans son talk ‘Subverting Windows 2003 SP1 Kernel Integrity Protection‘. Une version bridée de l’API NtSystemDebugControl est toujours accessible depuis l’user-land simplement avec le SeDebugPrivilege mais les fonctionnalités intéressantes sont accessibles uniquement en kernel-land à travers l’API exportée par le noyau : KdSystemDebugControl. Voici son prototype:

NTSTATUS
NTAPI
KdSystemDebugControl(
    SYSDBG_COMMAND Command,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength,
    PULONG ReturnLength,
    KPROCESSOR_MODE PreviousMode
);

Maintenant il faut comprendre comment Windbg fait pour parler à cette API depuis l’user-land. En fait c’est assez simple, il passe par un driver ! Il s’agit de %systemroot%\system32\kldgbdrv.sys qui agit en tant que wrapper pour KdSystemDebugControl. On retrouve le chargement de ce driver à l’aide du Service Control Manager dans la fonction LocalLiveKernelTargetInfo::InitDriver(void) de dbgeng.dll

Quelques infos sur ce driver :

lkd> !devobj \device\kldbgdrv
Device object (87bca750) is for:
 kldbgdrv \Driver\kldbgdrv DriverObject 898b59e0
Current Irp 00000000 RefCount 1 Type 00000022 Flags 00000040
Dacl 8b250ca0 DevExt 87bca808 DevObjExt 87bca810 
ExtensionFlags (0x00000800)  
                             Unknown flags 0x00000800
Device queue is not busy.

lkd> !drvobj \Driver\kldbgdrv 3
Driver object (898b59e0) is for:
 \Driver\kldbgdrv
Driver Extension List: (id , addr)

Device Object list:
87bca750  

DriverEntry:   94191e00	
DriverStartIo: 00000000	
DriverUnload:  94191a10	
AddDevice:     00000000	

Dispatch routines:
[00] IRP_MJ_CREATE                      94191a50	+0x94191a50
[01] IRP_MJ_CREATE_NAMED_PIPE           828f4537	nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE                       94191a50	+0x94191a50
[03] IRP_MJ_READ                        828f4537	nt!IopInvalidDeviceRequest
[04] IRP_MJ_WRITE                       828f4537	nt!IopInvalidDeviceRequest
[05] IRP_MJ_QUERY_INFORMATION           828f4537	nt!IopInvalidDeviceRequest
[06] IRP_MJ_SET_INFORMATION             828f4537	nt!IopInvalidDeviceRequest
[07] IRP_MJ_QUERY_EA                    828f4537	nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA                      828f4537	nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS               828f4537	nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    828f4537	nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION      828f4537	nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL           828f4537	nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         828f4537	nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL              94191a80	+0x94191a80
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     828f4537	nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN                    828f4537	nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL                828f4537	nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP                     828f4537	nt!IopInvalidDeviceRequest
[13] IRP_MJ_CREATE_MAILSLOT             828f4537	nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY              828f4537	nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY                828f4537	nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER                       828f4537	nt!IopInvalidDeviceRequest
[17] IRP_MJ_SYSTEM_CONTROL              828f4537	nt!IopInvalidDeviceRequest
[18] IRP_MJ_DEVICE_CHANGE               828f4537	nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA                 828f4537	nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA                   828f4537	nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP                         828f4537	nt!IopInvalidDeviceRequest

Grâce à la forme des IRP_MJ_* on devine que Windbg parle au driver avec des IOCTLs (classique). Justement voici l’ensemble des fonctions de la classe LocalLiveKernelTargetInfo de dbgeng.dll

LocalLiveKernelTargetInfo::CheckLowMemory(void)                                               
LocalLiveKernelTargetInfo::DebugControl(_SYSDBG_COMMAND,void *,ulong,void *,ulong,ulong *)                                                                                 
LocalLiveKernelTargetInfo::GetDescription(ushort *,ulong,ulong *)
LocalLiveKernelTargetInfo::GetTargetContext(ThreadInfo *,unsigned __int64,void *)
LocalLiveKernelTargetInfo::GetTargetKdVersion(_DBGKD_GET_VERSION64 *)
LocalLiveKernelTargetInfo::InitDriver(void)
LocalLiveKernelTargetInfo::Initialize(void)
LocalLiveKernelTargetInfo::LocalLiveKernelTargetInfo(void)
LocalLiveKernelTargetInfo::ReadBusData(ulong,ulong,ulong,ulong,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadControl(ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadIo(ulong,ulong,ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadMsr(ulong,unsigned __int64 *)
LocalLiveKernelTargetInfo::ReadPhysical(unsigned __int64,void *,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadVirtual(ProcessInfo *,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WaitForEvent(ulong,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteBusData(ulong,ulong,ulong,ulong,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteControl(ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteIo(ulong,ulong,ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteMsr(ulong,unsigned __int64)
LocalLiveKernelTargetInfo::WritePhysical(unsigned __int64,void *,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteVirtual(ProcessInfo *,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::`scalar deleting destructor'(uint)
LocalLiveKernelTargetInfo::~LocalLiveKernelTargetInfo(void)    

Cool ! Si Windbg à accès à tout ça on devrait nous aussi pouvoir en profiter. Reste à déterminer la forme des IOCTLs envoyés au driver. Pour cela on va reverser à la fois dbgeng.dll et kldbgdrv.sys.
Après avoir passé un peu de temps dessus on obtient les informations suivantes :

  • KdSystemDebugControl ne fonctionne que si KdDebuggerEnabled est à 1. Cette variable est mise à 1 par KdInitSystem() uniquement si on booté en DEBUG.
  • Le code IOCTL est 0x22C007 on est donc en METHOD_NEITHER. Je vous rassure tout est probé correctement, aussi bien dans le driver que par KdSystemDebugControl :p
  • Le driver vérifie que notre process a le SeDebugPrivilege à l’aide de SeSinglePrivilegeCheck
  • Le format du parametre lpInBuffer de DeviceIoControl est toujours le même, sous 32 bits on a :
    typedef struct _KLDBG
    {
    	SYSDBG_COMMAND DbgCommandClass;
    	PVOID DbgCommand;
    	DWORD DbgCommandLen;
    }KLDBG, * PKLDBG;

    nInBufferSize vaut donc 0xC. Quand aux paramètres lpOutBuffer et nOutBufferSize ils prennent les mêmes valeurs que les champs DbgCommand et DbgCommandLen;

  • On peut obtenir les définitions des SYSDBG_COMMAND dans le fichier kdtypes.h du NDK.

Il serait possible d’activer le debugger kernel en appelant KdEnableDebugger mais il faudrait charger un driver non signé pour cela. En 32 bits ca se fait bien tant qu’on ne veut pas le charger au boot mais en 64 bits c’est plus compliqué.

Maintenant il est temps de coder un POC. On va donc s’interfacer avec le driver et lui envoyer des IOCTLs. Mais d’abord on va vérifier plusieurs choses :

  • On active le SeDebugPrivilege.
  • On vérifie qu’on a booté en DEBUG avec la clé HKLM\System\CurrentControlSet\Control\SystemStartOptions.
  • On charge le driver à l’aide du Service Control Manager. Par contre c’est Windbg qui l’installe la première fois qu’on effectue un Live Kernel Debugging le POC donc le POC n’effectue qu’un simple StartService.
  • Ensuite on peut ouvrir le device \\.\kldbgdrv et lui envoyer des IOCTLs
  • On va demander 2 choses au driver, la première nous renvoyer la valeur du MSR IA32_SYSENTER_EIP (0×176) utilisé par SYSENTER et qui pointe sur nt!KiFastCallEntry puis de nous dumper 256 bytes à partir de cette mémoire noyau.

J’ai testé tout cela avec succès sur un Win7 32 bits à jour. Bien sur il faut lancer le binaire en tant qu’administrateur à cause de l’UAC. Je vous conseille de regarder le kdtypes.h du NDK afin de jouer avec les autres features de cette API. A noter qu’il ne faut pas modifier grand chose pour faire tourner la version 64 bits :p De plus en DEBUG PatchGuard n’est pas activé.

Vous trouvez le code et le binaire du POC ici :
http://ivanlef0u.fr/repo/kldbg.rar

Enjoy !

Autres refs :
Enabling the local kernel debugger on Vista RTM

Attacking the Windows Kernel

Bypassing PatchGuard on Windows x64

Subverting PatchGuard Version 2

PatchGuard Reloaded: A Brief Analysis of PatchGuard Version 3

Bypassing PatchGuard 3

Entry Filed under: RE

5 Comments

  • 1. Geo  |  mars 31st, 2010 at 07:52

    Tu vas demander le divorce avec Windows XP ? Quel dommage.

    Mais t’as raison, faut pas être à la traine donc au final c’est normal que tu te foutes sur Windows 7. D’ailleurs, l’environnement te plaît ?

    Merci pour l’article, sinon. Toujours aussi mad, à ce que je vois.

    Et sinon, question : \\.\kldbgdrv, tu appelles ça un device ? Ca veut dire que tu peux accéder à tout tes pilotes via un lien symbolique tel que \\.\blabla ?

    Cya o/

    Geo


  • 2. admin  |  mars 31st, 2010 at 13:52

    @Geo
    Merci :)
    Si le driver décide de créer un device et de le lié avec un SymbolicLink alors oui tu pourrais y accéder depuis l’user-land. C’est du NT Namespace.


  • 3. Vincent BOUZON  |  avril 2nd, 2010 at 08:52

    T’as enfin laché le vieux carosse ! Windows 7 ça change un peu ;) Au moins t’es pas passé sur Linux.


  • 4. Flopik  |  avril 5th, 2010 at 22:36

    Ca fais du bien des articles sur le reversing de Win7


  • 5. carol  |  janvier 22nd, 2012 at 16:46

    Hello!

    Great paper! Very usefull!

    But I’ve a question. Can I use FilterSendMessage instead of DeviceIoControl, if I’m using the minifilter technology?

    And why KdSystemDebugControl returns STATUS_INVALID_INFO_CLASS if I use it with the NDK on windows 7 32b?


Trackback this post


Calendar

mars 2017
L Ma Me J V S D
« fév    
 12345
6789101112
13141516171819
20212223242526
2728293031  

Most Recent Posts