Archive for mars, 2010

KdSystemDebugControl

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

5 comments mars 30th, 2010


Calendar

mars 2010
L Ma Me J V S D
« fév   avr »
1234567
891011121314
15161718192021
22232425262728
293031  

Posts by Month

Posts by Category