Posts filed under 'RE'

Segmentation Fault

Par manque d’inspiration le contenu de ce post racontera la manière dont je me suis prit une grosse baffe dans ma gueule en voulant jouer avec mon OS. Ma péripétie commence à cause d’une simple interrogation, le genre de choses qu’on se demande uniquement sous la douche le matin, après avoir absorbé sa dose vitale journalière de chocapics, des questions auxquelles seul un manuel Intel peut répondre mais qu’on à peur d’ouvrir tellement ce genre de ebooks fait de la peur. Le trip est simple, pourquoi un code userland ne peut-il lire à une adresse supérieur à 0x7FFFFFFF ? Ok vous allez me dire que c’est là qu’est chargé le kernel et ses copines, mais le vrai problème est de savoir par quel méchanisme magique le système nous balance une exception 0xC0000005 (STATUS_ACCESS_VIOLATION) dans la face.


Je ne pars pas complètement apwal, je sais que le passage de ring3 en ring0 se fait à travers l’instruction sysenter. De plus dans un précédent post (SYSENTER, stepping into da ring0) j’avais décrit son fonctionnement et notamment après avoir modifié le MSR_SYSENTER_EIP avec l’API NtSystemDebugControl j’ai pu exécuter du code qui avait accès à toute la mémoire. La clé est donc dans le fonctionnement de sysenter !

Après m’être rappelé que sysenter modifiait l’EIP mais aussi les segments CS et SS, je me suis dit que ceux-ci devaient avoir quelque chose d’intéressant à m’apprendre. Hop je prend un thread userland au hasard et je regarde ses segments.

userland segments

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000
0x1B=  11 0 11b

0x23= 100 0 11b

0x3B= 111 0 11b

En même temps je retrouve la structure décrivant un segment.

typedef struct _X86_SELECTOR

    {

    union

        {

        struct

            {

            WORD wValue;            // packed value

            WORD wReserved;

            };

        struct

            {

            unsigned RPL      :  2; // requested privilege level

            unsigned TI       :  1; // table indicator: 0=gdt, 1=ldt

            unsigned Index    : 13; // index into descriptor table

            unsigned Reserved : 16;

            };

        };

    }

    X86_SELECTOR, *PX86_SELECTOR, **PPX86_SELECTOR;

Un segment contient donc, son niveau de privilège requis, un bit désignant une table, soit la gdt ou la ldt et l’indice d’une structure X86_DESCRIPTOR dans cette table. Ici le RPL est bien à 3 (11b), le champ TI vaut 0, on est donc dans la GDT. Pour CS sont descriptor est à l’indice 3 alors que celui de SS est à l’indice 4.

Après je compare les segments avec ceux d’un thread tournant en kerneland.

kernelland segments

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000
0x08=   1 0 00b

0x10=  10 0 00b

0x23= 100 0 11b

0x30= 110 0 00b

Sysenter a fait son boulot, les segments CS et SS sont différents ainsi que FS mais ca c’est la fonction KiSystemService qui le redéfinit. Cette fois ci le descriptor de CS est à l’indice 1 dans la GDT et celui de SS à l’indice 2.

Sachant que un descriptor ressemble à :

typedef struct _X86_DESCRIPTOR

    {

    union

        {

        struct

            {

            DWORD dValueLow;        // packed value

            DWORD dValueHigh;

            };

        struct

            {

            unsigned Limit1   : 16; // bits 15..00

            unsigned Base1    : 16; // bits 15..00

            unsigned Base2    :  8; // bits 23..16

            unsigned Type     :  4; // segment type

            unsigned S        :  1; // type (0=system, 1=code/data)

            unsigned DPL      :  2; // descriptor privilege level

            unsigned P        :  1; // segment present

            unsigned Limit2   :  4; // bits 19..16

            unsigned AVL      :  1; // available to programmer

            unsigned Reserved :  1;

            unsigned DB       :  1; // 0=16-bit, 1=32-bit

            unsigned G        :  1; // granularity (1=4KB)

            unsigned Base3    :  8; // bits 31..24

            };

        };

    }

    X86_DESCRIPTOR, *PX86_DESCRIPTOR, **PPX86_DESCRIPTOR;

Après avoir vu ca, j’ai pas vraiment eu envie de dumper la GDT à la main, j’ai trouvé une extension ProtMode pour windbg qui permet d’afficher le contenu d’un X86_DESCRIPTOR de manière plus jolie.

A noter que la GDT peut etre retrouvé soit avec l’instruction sgdt, soit dans le KPCR avec la commande !pcr du KD.

kd> !ProtMode.Descriptor GDT 1

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x01, Descriptor @ 0x8003f008

8003f008 ff ff 00 00 00 9b cf 00

Segment size is in 4KB pages, 32-bit default operand and data size

Segment is present, DPL = 0, Not system segment, Code segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0x00000000

Target code segment size = 0x000fffffkd> !ProtMode.Descriptor GDT 2

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x02, Descriptor @ 0x8003f010

8003f010 ff ff 00 00 00 93 cf 00

Segment size is in 4KB pages, 32-bit default operand and data size

Segment is present, DPL = 0, Not system segment, Data segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0x00000000

Target code segment size = 0x000fffff

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x03, Descriptor @ 0x8003f018

8003f018 ff ff 00 00 00 fb cf 00

Segment size is in 4KB pages, 32-bit default operand and data size

Segment is present, DPL = 3, Not system segment, Code segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0x00000000

Target code segment size = 0x000fffff

kd> !ProtMode.Descriptor GDT 4

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x04, Descriptor @ 0x8003f020

8003f020 ff ff 00 00 00 f3 cf 00

Segment size is in 4KB pages, 32-bit default operand and data size

Segment is present, DPL = 3, Not system segment, Data segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0x00000000

Target code segment size = 0x000fffff

Comme prévu le premier descriptor est de type Code segment et possède un DPL (Descriptor Privilege Level) à 0, notre segment CS en ring0.
Le second descriptor, segment SS ring0.
Le troisième, segment CS ring3
Le quatrième, segment SS ring3.

Ici les segments sont en Flat-model, c’est à dire qu’ils décrivent entièrement l’espace mémoire (segment size*4KB).

Par contre pour le segment FS, indice 7 dans la GDT pour le userland.

kd> !ProtMode.Descriptor GDT 7

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x07, Descriptor @ 0x8003f038

8003f038 ff 0f 00 e0 fd f3 40 7f

Segment size is in bytes, 32-bit default operand and data size

Segment is present, DPL = 3, Not system segment, Data segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0x7ffde000

Target code segment size = 0x00000fff

L’adresse de base est 0x7ffde000 qui correspond à l’adresse du TEB du thread courant, attention depuis xp sp2 cette adresse est randomisé pour chaque threads, mais c’est pas grave avec fs:[0] on la retrouve :p

Pour un thread ring0 le segment FS vaut :

kd> !ProtMode.Descriptor GDT 6

------------------- Code Segment Descriptor --------------------

GDT base = 0x8003F000, Index = 0x06, Descriptor @ 0x8003f030

8003f030 01 00 00 f0 df 93 c0 ff

Segment size is in 4KB pages, 32-bit default operand and data size

Segment is present, DPL = 0, Not system segment, Data segment

Segment is not conforming, Segment is readable, Segment is accessed

Target code segment base address = 0xffdff000

Target code segment size = 0x00000001

En fs:[0] on retrouve l’adresse de la structure KPCR, ca peut toujours servir, notamment dans un shellcode :]

Lorsque j’essayais de lire une adresse supérieure à 0x7FFFFFFF avec un thread ring3 je me mangeais une exception car j’essayais d’accéder à une kernel page alors que mon CPL (Current Privilege Level) était à 3. La MMU génère une interruption 14 (Page Fault) qui est gérée par la routine de l’IDT KiTrap0E puis par MmAccessFault.

Je me suis dit qu’il suffisait de modif les segments à la main, comme le fait sysenter, pour avoir un thread tournant en ring0 (Il est con le Ivan parfois quand même ….). Sous Olly c’est possible, alors je lance un programme, modif ses segments pour avoir le CS et SS pareil qu’un thread ring0, je relance le thread et là c’est le drame ! Je me mange une exception. Evidemment je m’y attentais quand même un peu, ca paraissait un peu trop facile :) Par contre il me fallait comprendre pourquoi l’exception avait été générée.

Arrivé à ca point, pas le choix, faut regarder les man Intel, pénible corvée qui doit se faire en portant une combinaison NBC et des chaussettes marsupilami. Après quelques recherches, j’apprends que depuis tout petit on me ment, les segments ne font pas que 16 bits !! Il existe tout une partie cachée qui contient notamment le DPL du segment ! En fait cela permet d’éviter au processeur des cycles en plus de lecture pour lire la GDT. Ce cache ne peut être modifié directement mais des instructions comme sysenter le mettent à jour, comme le montre le manuel de l’instruction :

CS.SEL  <- SYSENTER_CS_MSR (* Operating system provides CS *)

(* Set rest of CS to a fixed value *)

CS.BASE  <- 0; (* Flat segment *)

CS.LIMIT <-  FFFFFH; (* 4-GByte limit *)

CS.ARbyte.G <-  1; (* 4-KByte granularity *)

CS.ARbyte.S  <- 1;

CS.ARbyte.TYPE  <- 1011B; (* Execute   Read, Accessed *)

CS.ARbyte.D <-  1; (* 32-bit code segment*)

CS.ARbyte.DPL <-  0;

CS.SEL.RPL <-  0;

CS.ARbyte.P <-  1;

CPL <-  0;

Donc même après avoir modifié le segment selector, le système remarquant que le RPL du segment ne correspond pas au DPL contenu dans le cache nous balance une interruption dans la face. Ouinn ….

Bref j’ai prit une baffe mais j’ai compris pourquoi :]

Références :
http://www.awprofessional.com/articles/article.asp?p=167857&rl=1

http://www.codeguru.com/cpp/w-p/system/devicedriverdevelopment/article.php/c8223__1/

http://www.summitsoftconsulting.com/SampleCode/ProtMode.zip

http://www.internals.com/articles/protmode/protmode.htm

http://www.sandpile.org/ia32/sreg.htm

3 comments mai 5th, 2007

Playing With Windows Handles

Hello, voici encore un tool sortit de mon esprit torturé. Il permet d’énumerer les handles ouverts pas un process, pratique par exemple pour connaîtres les fichiers ouvert par un process.


Le programme utilise principalement l’API native de Win, il fonctionne de la manière suivante :

- On récupère le nombre total de handles ouvert (NtQuerySystemInformation), puis on alloue la mémoire sur HandleCount*sizeof(SYSTEM_HANDLE_INFORMATION), SYSTEM_HANDLE_INFORMATION est une structure qui contient :

typedef struct _SYSTEM_HANDLE_INFORMATION { // Information Class 16
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;  // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;

On peut ainsi trouver les handles ouvert par notre process en conaissant son PID.

- On duplique le handle (NtDuplicateHandle) pour que notre process puisse récupérer des informations desssus, la fonction NtQueryObject :

extern "C" NTSTATUS __stdcall NtQueryObject(
IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL
);

est utilisée avec les ObjectInformationClass ObjectTypeInformation et ObjectNameInformation, avec le premier on trouve (je pense que vous l’avez deviné) le type de l’objet qui peut être :

(phrack 59-0x10 by crazylord)
* Adapter       * File             * Semaphore
* Callback      * IoCompletion     * SymbolicLink
* Controler     * Job              * Thread
* Desktop       * Key              * Timer
* Device        * Mutant           * Token
* Directory     * Port             * Type
* Driver        * Process          * WaitablePort
* Event         * Profile          * WindowStation
* EventPair     * Section          * WmiGuid

Puis on récup le nom de l’object et on affiche tout ca.

Bon jusqu’ici c’est simple, le problème c’est que je suis tomber sur un bug lorsqu’on esssaye d’obtenir le nom sur un NamedPipe ouvert en mode synchrone (gnifulol?) la fonction NtQueryObject devient blocante oO. J’ai donc implémenté un thread qui effectue cette opération pour ce type (de type File en fait), au moins il peut bloqué puisqu’on le kill si il fait le malin ;).

D’après ce que j’ai vu sur d’autres programme qui font la même, soit ceux ci utilisent directement un driver pour récup des infos sur les objets, soit ils sont codé par des autruches, sisi aller voir la et dites moi ce que vous pensez du code http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827.

Le but de ce tool est surtout de montrer la puissance de l’API native qui permet de faire plein plein de choses qu’on ne peut faire avec les API documentées par le SDK.
Allay un petit exemple :

C:\\ProgHack\\c\\Handles>handle explorer.exe
Process Handle Enumeration By IvanlefOu
Be MAD!
Process : explorer.exe
Total Handles : 5368

KeyedEvent : \\KernelObjects\\CritSecOutOfMemoryEvent
Directory : \\KnownDlls
File : \\Documents and Settings\\Moi
Section :
Directory : \\Windows
[...]

Le binaire et le code dispo dans le pack !
http://ivanlef0u.fr/repo/Handle.rar

Pour aller plus loin sachez que chaque process à sa propre table de handles, celle ci se trouve pointé dans la structure _EPROCESS (on est ds le noyau la pour ceux qui se touchent le pipi au fond) :

lkd> dt nt!_EPROCESS
[...]
+0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE
[...]

ObjectTable pointe sur une structure _HANDLE_TABLE

lkd> dt nt!_HANDLE_TABLE
+0x000 TableCode        : Uint4B
+0x004 QuotaProcess     : Ptr32 _EPROCESS
+0x008 UniqueProcessId  : Ptr32 Void
+0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
+0x01c HandleTableList  : _LIST_ENTRY
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
+0x02c ExtraInfoPages   : Int4B
+0x030 FirstFree        : Uint4B
+0x034 LastFree         : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount      : Int4B
+0x040 Flags            : Uint4B
+0x040 StrictFIFO       : Pos 0, 1 Bit

HandleTableList est une double liste chainée sur les _HANDLE_TABLE des autres processus, on peut la parcouri en utilisant la commande suivante sous le kernel debugger :

!list -x "dt nt!_HANDLE_TABLE @$extret-1C" 0x(addresse de départ)

Chez moi ca donne :

lkd> !list -x "dt nt!_HANDLE_TABLE @$extret-1C" 0xe1a4f384
+0x000 TableCode        : 0xe1103001
+0x004 QuotaProcess     : 0x8173a750 _EPROCESS
+0x008 UniqueProcessId  : 0x000000b4
+0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
+0x01c HandleTableList  : _LIST_ENTRY [ 0xe1ab18b4 - 0xe17eb4fc ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo        : (null)
+0x02c ExtraInfoPages   : 0
+0x030 FirstFree        : 0x19c
+0x034 LastFree         : 0x774
+0x038 NextHandleNeedingPool : 0x1000
+0x03c HandleCount      : 469
+0x040 Flags            : 0
+0x040 StrictFIFO       : 0y0

+0x000 TableCode        : 0xe1165001
+0x004 QuotaProcess     : 0x815db758 _EPROCESS
+0x008 UniqueProcessId  : 0x0000069c
+0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
+0x01c HandleTableList  : _LIST_ENTRY [ 0xe151a88c - 0xe1a4f384 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo        : (null)
+0x02c ExtraInfoPages   : 0
+0x030 FirstFree        : 0x874
+0x034 LastFree         : 0
+0x038 NextHandleNeedingPool : 0x1000
+0x03c HandleCount      : 553
+0x040 Flags            : 0
+0x040 StrictFIFO       : 0y0

+0x000 TableCode        : 0xe1182000
+0x004 QuotaProcess     : 0x813f29c0 _EPROCESS
+0x008 UniqueProcessId  : 0x000005a0
+0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
+0x01c HandleTableList  : _LIST_ENTRY [ 0xe1f806bc - 0xe1ab18b4 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo        : (null)
+0x02c ExtraInfoPages   : 0
+0x030 FirstFree        : 0x190
+0x034 LastFree         : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount      : 129
+0x040 Flags            : 0
+0x040 StrictFIFO       : 0y0

Merci à fuzen_op de rootkit.com
http://www.rootkit.com/newsread.php?newsid=307

Alors ou qu’ils sont nos handles ! He bien ils sont dans un tableaux pointé par TableCode qui contient des structures _HANDLE_TABLE_ENTRY !

lkd> dt nt!_HANDLE_TABLE_ENTRY
+0x000 Object           : Ptr32 Void
+0x000 ObAttributes     : Uint4B
+0x000 InfoTable        : Ptr32 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value            : Uint4B
+0x004 GrantedAccess    : Uint4B
+0x004 GrantedAccessIndex : Uint2B
+0x006 CreatorBackTraceIndex : Uint2B
+0x004 NextFreeTableEntry : Int4B

Qui plus précisement ressemble à:

/* From the free version of ntifs.h */
typedef struct _HANDLE_TABLE_ENTRY {
union {
PVOID Object;
ULONG ObAttributes;
PHANDLE_TABLE_ENTRY_INFO InfoTable;
ULONG Value;
};
union {
ULONG GrantedAccess;
struct {
USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

La struture ne contient pas directement l’addr de l’objet en fait comme on peut le voir :

Pour plus d’infos allez voir le chapitre Object Manager de Windows Internals. Pour récup le pointeur sur l’object header il faut donc use le chtit code suivant

#include 

int main(int argc, char * argv[])
{
ULONG handle=0;

handle=strtoul(argv[1], NULL, 0);

handle|=0x80000000; handle&=0xfffffff8;

printf("Object header is at : 0x%x\n", handle);

return 0;
}

Le pointeur sur object hearder pointe sur une structure (_OBJECT_HEADER)

lkd> dt nt!_OBJECT_HEADER
+0x000 PointerCount     : Int4B
+0x004 HandleCount      : Int4B
+0x004 NextToFree       : Ptr32 Void
+0x008 Type             : Ptr32 _OBJECT_TYPE
+0x00c NameInfoOffset   : UChar
+0x00d HandleInfoOffset : UChar
+0x00e QuotaInfoOffset  : UChar
+0x00f Flags            : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body             : _QUAD

Ce qui nous intéresse est la structure _OBJECT_TYPE :

lkd> dt nt!_OBJECT_TYPE
+0x000 Mutex            : _ERESOURCE
+0x038 TypeList         : _LIST_ENTRY
+0x040 Name             : _UNICODE_STRING
+0x048 DefaultObject    : Ptr32 Void
+0x04c Index            : Uint4B
+0x050 TotalNumberOfObjects : Uint4B
+0x054 TotalNumberOfHandles : Uint4B
+0x058 HighWaterNumberOfObjects : Uint4B
+0x05c HighWaterNumberOfHandles : Uint4B
+0x060 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+0x0ac Key              : Uint4B
+0x0b0 ObjectLocks      : [4] _ERESOURCE

On a le type de l’objet ainsi que son nom.
Donc si on voudrais virer l’acces à un handle il faudrait scanner cette table pour trouver lequel puis suprimer l’entrée.
Après je ne sais pas comment va réagir le système mais l’idée est la.
Je testerais cela prochainement je pense.

Ivanlef0u

15 comments janvier 11th, 2007

Next Posts


Calendar

mars 2024
L Ma Me J V S D
« fév    
 123
45678910
11121314151617
18192021222324
25262728293031

Posts by Month

Posts by Category