Unlocker

mars 2nd, 2008 at 03:30 admin

Un comment laissé sur mon blog récemment a attiré mon attention, j’ai donc décidé de répondre par mail à son auteur. Après plusieurs échanges j’ai décidé de compiler mes découvertes et de vous en faire profiter.

Yo Regis, j’ai vu votre comment et j’ai décidé de regarder d’un peu plus près le tool unlocker afin de comprendre son fonctionnement. Je l’ai donc installé en VM et analysé principalement avec IDA et ollydbg. J’étais vraiment curieux alors j’ai poussé mes investigations un peu loin, à vous de juger, en tout cas unlocker n’a plus beaucoup de secrets pour moi à présent, modulo les erreurs que j’ai pu faire bien sur. Voici ce que j’ai compris de son fonctionnement, attention prenez votre respiration, c’est partit !

Les fichiers exécutables sont mit dans « C:\Program Files\Unlocker », on y trouve :

Unlocker.exe
UnlockerAssistant.exe
UnlockerCOM.dll
UnlockerHook.dll
UnlockerDriver5.sys

Alors après une petite analyse j’ai établit les dépendances :

Unlocker.exe -> UnlockerDriver5.sys
UnlockerAssistant.exe -> UnlockerHook.dll

UnlockerCOM.dll est chargé dans explorer.exe avec les shellextension utilisant les clés :
{DDE4BEEB-DDE6-48fd-8EB5-035C09923F83}
Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved
folder\\shellex\\ContextMenuHandlers\\UnlockerShellExtension
AllFileSystemObjects\\shellex\\ContextMenuHandlers\\UnlockerShellExtension
software\\classes\\clsid\\UnlockerShellExtension

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{DDE4BEEB-DDE6-48fd-8EB5-035C09923F83} contient par exemple
defaut : C:\Program Files\Unlocker\UnlockerCOM.dll
ThreadindModel : Apartment

Ces clés sont mises en place lors de l’installation.

Pour plus d’infos sur les extensions shell voir : Adding Explorer Bars
et la prog COM : Step by Step COM Tutorial

Ok, ca c’est pour le shell, qui est en fait juste une interface pour lancer le Unlocker.exe.

Sinon j’ai remarqué que UnlockerHook.dll au moment d’être chargé par UnlockerAssistant.exe va installer des hooks de messages de type WH_CBT (création de fenêtres..) avec l’api SetWindowsHookEx. Quand elle est injecté dans un nouveau process elle va mettre un inline hook sur la fonction SHFileOperationW mais uniquement lorsque quelle se retrouve injecté dans explorer.exe. Je n’ai pas trop bien compris a quoi servait le hook surtout que les valeurs de retour de la fonction ne sont pas documentées, je pense que le développeur a dur étudier le comportement de la fonction et comprendre par lui même la signification de certaines valeurs de retour. De toute façon ce n’est pas là qu’est le coeur du fonctionnement du soft. Avec plus de recherches il y a moyen de comprendre pourquoi il hook cette fonction imo.

Bon, en fait le problème c’est les handles qui sont dit « locker » mais qu’est ce que cela veut dire ? Si on regarde le prototype de CreateFile on a :

HANDLE WINAPI CreateFile(
__in LPCTSTR lpFileName,
__in DWORD dwDesiredAccess,
__in DWORD dwShareMode,
__in LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in DWORD dwCreationDisposition,
__in DWORD dwFlagsAndAttributes,
__in HANDLE hTemplateFile
);

Le param dwShareMode correspond à la façon dont le handle pourra être partagé entre les processus. Dans le cas ou celui ci vaut 0, le handle ne pourra pas être ouvert par un autre process tant qu’il ne sera pas fermé par CloseHandle par le process qui l’a ouvert en premier. C’est le kernel qui effectue c’est vérification et qui renvoie une erreur au subsytem disant « Le fichier est utiliser par une autre application blablaba… »

Le programme principal se situe donc dans Unlocker.exe, lorsqu’on veut « unlocker » un fichier son path est passé en argument au programme qui va ensuite procéder de 2 façons :

- Dans le cas ou le fichier est une DLL, le tool va crée un utiliser CreateToolhelp32Snapshot pour avoir une « photo » de tous les process lancés (option TH32CS_SNAPPROCESS) puis énumérer les dll chargées dans chacun en faisant aussi un snapshot mais cette fois avec l’option TH32CS_SNAPMODULE. A partir de cette liste il va chercher dans chacun des process le nom des modules puis voir s’il trouve la DLL concernée. Si oui alors il injecte un code dans le process avec le triplet VirtualAllocEx/WriteProcessMemory/CreateRemoteThread. Le code injecté va donc se retrouver dans le process qui a chargé la DLL puis va utiliser GetModuleHandle pour obtenir un handle dessus et FreeLibrary pour la décharger du processus.

Remarquez que je trouve un peu con de faire comme ca car le handle sur la DLL est en fait son ImageBase, qu’on peut très bien obtenir depuis le snapshot, il suffirait donc d’injecter un thread pointant sur FreeLibrary avec en argument l’ImageBase de la DLL pour la décharger.

- Dans le cas d’un fichier « normal », le tool procède d’une manière différente. Pour pouvoir unlocker le fichier, il doit fermer le handle qui est ouvert par un processus X. Cette fois ci il s’agit d’un « vrai » handle, c’est à dire qu’il fait référence à un objet noyau contrairement à un handle sur une DLL qui n’est juste qu’une adresse sur le PE header de celle ci en mémoire. Bref, l’idée consiste donc à énumérer les handles de chaque process, de la scanner afin de trouver le ou les process qui ont ouvert le handle sur ce fichier. Le problème c’est que sous Windows, on ne peut simplement obtenir la liste des handles d’un process, il faut en fait les récupérer dans la liste de TOUS les handles ouvert par le système. Dans le cas de unlocker celui-ci utilise l’API ZwQuerySystemInformation avec l’InformationClass SystemHandleInformation, cette fonction nous renvoie une liste de structures SYSTEM_HANDLE_INFORMATION qui sont de la forme :

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

Unlocker va utiliser 3 champs de cette structure :

- ProcessId, le PID du process qui à ouvert le handle
- Handle, la valeur du handle dans le context du process qui l’a ouvert (n’oubliez pas, un handle est spécifique uniquement dans le contexte du process qui l’a ouvert !)
- Object, l’adresse dans le kernel de l’objet, pour un fichier c’est un pointeur sur une structure FILE_OBJECT par exemple.

Unlock va d’abord ouvrir un handle sur le process à partir de son PID avec les flags VM_READ|DUP_HANDLE|QUERY_INFORMATION puis utiliser l’api DuplicateHandle, cette api permet comme son nom l’indique de copier un handle dans le contexte d’un autre, c’est très pratique, en effet unlocker va copier le handle dans son contexte afin de pouvoir le manipuler et obtenir des infos dessus, DuplicateHandle est appelé avec les params suivant (extrait de ollydbg) :
00126F94 00000050 |hSourceProcess = 00000050 (window) <-PID du process visé
00126F98 000000C8 |hSource = 000000C8 (window) <- valeur du handle
00126F9C FFFFFFFF |hTargetProcess = FFFFFFFF <- handle de notre process,
00126FA0 0012B654 |phTarget = 0012B654 <- pointeur sur le futur handle
00126FA4 00000000 |Access = 0
00126FA8 00000000 |Inheritable = FALSE
00126FAC 00000000 \Options = 0

Windows autorise le fait de dupliquer le handle, cela veut dire que si on duplique le handle d’un fichier ouvert avec dwShareMode à 0 dans notre process, on se retrouve avec les même accès que le process maître sur le fichier. C’est assez particulier comme fonctionnement je l’avoue mais Windows l’autorise donc c’est cool. En gros, si je duplique un handle « locker » de fichier dans mon process, je peux très bien par la suite lire et écrire dedans comme si j’étais le process original (dépends des droits mit dans dwDesiredAccess au départ).

Une fois que le handle est « copié » dans le contexte de unlocker.exe, le tool va enfin utilise son driver. Après analyse sous IDA il apparait que le driver va servir à retrouver le nom de l’objet référencé dans le kernel. En effet, unlocker va communiquer avec le driver à travers les api WriteFile et ReadFile. La première api sert à envoyer au driver une structure contenant le handle visé et le pointeur sur l’objet kernel, cette structure à donc de la forme de :

typedef struct _RequestObjectName
{
HANDLE Handle;
PVOID Object;
}RequestObjectName, * PRequestObjectName;

Quand le driver reçoit cette structure il va attendre l’appel de l’api ReadFile pour utiliser l’api kernel, ObReferenceObjectByHandle sur le paramètre Handle de la structure RequestObjectName pour retrouver le pointeur sur l’objet dans le kernel. Il le compare ensuite avec le champ Object de la structure pour être sur que unlocker à demander d’analyser cet objet précis puis appel la fonction ObQueryNameString pour obtenir le nom de l’objet qui sera recopier dans un buffer user-land sous forme de structure UNICODE_STRING.

De retour dans unlocker, le code continue d’énumérer ainsi les process ayant ouvert un handle sur le fichier concerné et les énumère dans la listbox.

Quand on demande d’unlocker un fichier, le tool va injecter un thread dans le process visé qui va utiliser le même code que celui qui sert à décharger la une DLL. En fait ce code est assez marrant, il fonctionne de cette façon :

Le code injecté se retrouve soit avec le path de la dll à décharger soit avec la valeur du handle dans le contexte du process visé. Il utilise uniquement 3 fonctions, GetModuleHandle, FreeLibrary et CloseHandle. Il commence par récup un handle sur la DLL avec GetModuleHandle puis entre dans une boucle de 16 itérations, qui va appeler FreeLibray et CloseHandle. Si le développeur à choisit d’utiliser une boucle c’est parce que chaque DLL possède un compteur référençant le nombre de chargements effectués avec LoadLibrary, en faisant une boucle, le tool va décrémenter le compteur (à travers FreeLibrary) référençant le nombre d’appel de LoadLibray sur cette DLL, quand ce compteur atteint 0, FreeLibray décharger la DLL. Dans le cas d’un simple handle, le GetModuleHandle va foirer (son retour n’est pas checké), ce n’est pas grave, le CloseHandle va le fermer ensuite et quitter la boucle.

Voici le code vu sous IDA :

; =============== S U B R O U T I N E =======================================
.text:004105B1
; Attributes: bp-based frame

sub_4105B1 proc near                    ; DATA XREF: sub_41178F+163 o
                                        ; sub_41178F+186 o
compteur= dword ptr -4
remoteinfos= dword ptr  8

	push ebp      ; remotesinfos struct :
                  ; +0 @FreLibrary
                  ; +4 @GetModuleHandle
                  ; +8 @CloseHandle
                  ; +C handle/full dll path
	mov ebp, esp
	push ecx
	and [ebp+compteur], 0
	push ebx
	push esi
	mov esi, [ebp+remoteinfos]
	push edi
	lea ebx, [esi+0Ch]
	push ebx
	call dword ptr [esi+4]              ; GetModuleHandle
	jmp short loc_4105DC

.text:004105C8                         loc_4105C8:                             ; CODE XREF: sub_4105B1+2F j
	cmp [ebp+compteur], 16
	jge short loc_4105E2
	push edi
	call dword ptr [esi]                ; FreeLibrary
	push edi
	call dword ptr [esi+8]              ; CloseHandle
	push ebx
	call dword ptr [esi+4]              ; GetModuleHandle
	inc [ebp+compteur]

loc_4105DC:                             ; CODE XREF: sub_4105B1+15 j
	mov edi, eax
	test edi, edi
	jnz short loc_4105C8

loc_4105E2:                             ; CODE XREF: sub_4105B1+1B j
	pop edi
	pop esi
	xor eax, eax
	pop ebx
	leave
	retn 4
sub_4105B1 endp

Cependant DuplicateHandle nous fournit une option intéressante, DUPLICATE_CLOSE_SOURCE, qui comme son nom l’indique permet de fermer le handle directement à la source. L’auteur lui à choisit une autre méthode pour fermer le handle, il injecte un petit code dans le process qui l’a ouvert, pourquoi pas …

Le plus simple que je te conseil est de directement crée un thread avec CreateRemoteThread sur la fonction CloseHandle dans le process visé, en mettant en paramètre de ce thread la valeur du handle à fermer, cela devrait fonctionner à merveille.

Remarquez que pour copier un fichier unlocker va aussi injecter un shellcode qui va directement crée la copie depuis le process ayant ouvert le handle sur le fichier, pour s’en rendre compte il suffit de voir quelles APIs il fournit au shellcode injecté :

push    offset aCreatefilew ; "CreateFileW"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_818], eax
call    esi ; GetProcAddress
push    offset aSetfilepointer ; "SetFilePointer"
push    [ebp+hModule]   ; hModule
mov     [ebp+Buffer], eax
call    esi ; GetProcAddress
push    offset aReadfile ; "ReadFile"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_840], eax
call    esi ; GetProcAddress
push    offset aWritefile ; "WriteFile"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_83C], eax
call    esi ; GetProcAddress
push    offset aClosehandle ; "CloseHandle"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_838], eax
call    esi ; GetProcAddress
push    offset aGlobalalloc ; "GlobalAlloc"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_834], eax
call    esi ; GetProcAddress
push    offset aGlobalfree ; "GlobalFree"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_830], eax
call    esi ; GetProcAddress
push    offset aGetfilesize ; "GetFileSize"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_82C], eax
call    esi ; GetProcAddress
push    offset aSleep   ; "Sleep"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_828], eax
call    esi ; GetProcAddress
push    offset aGetoverlappedr ; "GetOverlappedResult"
push    [ebp+hModule]   ; hModule
mov     [ebp+var_824], eax
call    esi ; GetProcAddress
push    offset aGetlasterror ; "GetLastError"

Je vous laisse imaginer son fonctionnement ….

Pareil pour la copie je pense qu’il suffit juste de faire un DuplicateHandle sur le fichier puis de recopier son contenu depuis notre process.

Pour les actions de renommer et déplacer, unlocker va simplement fermer le handle puis renommer ou déplacer le fichier après.

Bon alors qu’est ce que j’en pense de tout ca, je pense que pour décharger un DLL, unlocker fait bien le boulot, c’est la méthode la plus simple. Par contre dans le cas d’un fichier, l’utilisation d’un driver un peu lourde, il est possible de faire beaucoup plus simple, en effet pour obtenir le nom de l’objet référencé par un handle après l’avoir dupliqué il suffit d’utiliser l’API ZwQueryObject de prototype :

NTSTATUS NtQueryObject(
__in_opt HANDLE Handle,
__in OBJECT_INFORMATION_CLASS ObjectInformationClass,
__out_opt PVOID ObjectInformation,
__in ULONG ObjectInformationLength,
__out_opt PULONG ReturnLength

Il faut l’utiliser avec ObjectInformationClass ObjectTypeInformation et ObjectNameInformation pour obtenir le type et le nom de l’objet référencé par le handle. Cela est très pratique car c’est un syscall Windows et cela évite de coder un driver exprès. J’en veux pour preuve un post que j’ai rédigé il y a déjà quelques temps Playing With Windows Handles et celui de mon padawan Close a remote handle file. Je pense qu’avec ces codes vous avez tout en main pour réaliser un programme mieux que unlocker ;)

J’ai aussi remarqué une chose intéressant, le tool utilise le device LanmanRedirector pour retrouver les fichiers ouverts par les utilisateurs distant, j’ai découvert ca d’abord sous IDA puis après avoir googlé je suis tombé sur cet article, je n’ai malheureusement pas trouvé de code mettant en place ce processus mais avec une analyse plus profonde de unlocker je pense qu’il n’est pas très difficile de recoder cela.

Ouf, j’espère ne pas trop vous avoir soulé avec mes explications, en tout cas j’ai passé un peu de temps à reverser ce tool, ca m’a fait plaisir. Voilà, vous voyez le reverse c’est cool, mangez en :)

Entry Filed under: RE

15 Comments

  • 1. dora  |  mars 2nd, 2008 at 16:00

    Comme les chocapics !


  • 2. SynApsus  |  mars 3rd, 2008 at 07:12

    J’adooore ce genre de tites analyses :) pour une fois g pas besoin de doliprane en plus.
    Continue à ecrire ivan, ça me permet de continuer à rever que je referai des trucs dans le genre bientot !


  • 3. Orkblutt  |  mars 3rd, 2008 at 09:52

    On peut retrouver l’utilisation de QueryObject dans un vieux src ecrit par Zoltan Csizmadia en 2000. Sa classe SystemHandleInformation peut être trés utile pour ceux qui veulent ecrire un programme à la unlocker…
    Une petite mise à jour par Shub-Nigurrath from ARTeam: http://arteam.accessroot.com/releases.html?fid=17


  • 4. x  |  mars 10th, 2008 at 00:41

    « Par contre dans le cas d’un fichier, l’utilisation d’un driver un peu lourde, il est possible de faire beaucoup plus simple, en effet pour obtenir le nom de l’objet référencé par un handle après l’avoir dupliqué il suffit d’utiliser l’API ZwQueryObject »

    ok mais dans ton exemple (http://ivanlef0u.free.fr/repo/Handle.rar) tu utilises NtQueryInformationFile(FileNameInformation) et pas ZwQueryObject(ObjectNameInformation) (ni NtQueryObject) : pourquoi ? samarchpa?
    je demande paske NtQueryInformationFile(FileNameInformation) ne donne pas le volume du fichier…pas cool
    on fait comment pour avoir le volume sur lequel est le fichier sans driver (et proprement) ?


  • 5. marylune  |  mars 10th, 2008 at 16:02

    écoute avec tes « je trouve un peu con » et tes « il suffirait » qu’estce que tu fais de ta Vie?
    moi je comprends rien a ce que tu expliques parceque tu ne donnes rien en échange

    cordiale salutations IvanleFou

    Marylune


  • 6. Cedrick  |  mars 10th, 2008 at 17:55

    “Par contre dans le cas d’un fichier, l’utilisation d’un driver un peu lourde, il est possible de faire beaucoup plus simple, en effet pour obtenir le nom de l’objet référencé par un handle après l’avoir dupliqué il suffit d’utiliser l’API ZwQueryObject”

    Typique Fanfaronade Francaise :D Quelle meilleure solution avez vous?

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

    C’est vraiment pas terrible ca, mais je comprends que vous ayez eu cette idee puisque c’est la plus facile a mettre en place. Cependant hormis son inelegance, terminer les threads bloques ne nettoie pas les requetes bloquees, mais aussi car en user space beaucoup de handle ne trouvent pas de nom.

    Le driver est malheureusement la meilleure solution. Si vous trouvez mieux tennez moi au courrant :D


  • 7. Cedrick  |  mars 10th, 2008 at 18:03

    Zut j’ai clique « Submit » trop tot.

    Felicitation pour la tres bonne reversion de Unlocker, ca donne en effet les grandes lignes correctes.


  • 8. admin  |  mars 10th, 2008 at 20:15

    @x
    Tu peux récupérer le full path d’un fichier à partir de son handle avec GetFullPathName

    @Cedrick
    Nan je n’ai pas mieux en effet, je voulais juste dire que je trouvais cela lourd et non que c’était mal implémenté, au contraire même. Process explorer fonctionne de la même manière à ce niveau, il utilise aussi un driver. Si jamais je vois mieux je vous mail :]

    @Marylune
    Dans ma vie je bois du brawndo, ça déchire ta soif et ça contient des electrolytes !


  • 9. marylune  |  mars 10th, 2008 at 23:53

    http://sendtofriend.blogspot.com/2007/12/brawndo-du-film-idiocracy.html ?


  • 10. admin  |  mars 11th, 2008 at 00:02

    Wai le brawndo de idiotcracy :)
    http://www.mirror-mondenocturne.com/films-idiocracy-dvdrip-vf-1-103


  • 11. x  |  mars 11th, 2008 at 00:46

    @ivanlef0u : j’ai trouve (un truc bien crade), mais merci quant meme pour la blague.

    sinon…
    « Il commence par récup un handle sur la DLL avec GetModuleHandle puis entre dans une boucle de 16 itérations, qui va appeler FreeLibray et CloseHandle. Si le développeur à choisit d’utiliser une boucle c’est parce que chaque DLL possède un compteur référençant le nombre de chargements effectués avec LoadLibrary »
    et pis apres
    « je pense que pour décharger un DLL, unlocker fait bien le boulot, c’est la méthode la plus simple »
    ok : ya 1 compteur (maybe OBJECT_BASIC_INFORMATION.ReferenceCount ?), on le lit pa, on fait 16 tours mem si le compteur est a 17 et sa s’appel sa bien faire le boulot ? au bou de 16 win devine kon veut unloader la DLL cé sa et le 17 cé cado ?


  • 12. admin  |  mars 11th, 2008 at 00:59

    Mais nan, la boucle sert uniquement au compteur qui référence le nombre de fois que la DLL a été loadée. Il se situe dans le champ LoadCount de la structure LDR_MODULE unique à cahque DLL accessible depuis une double listé chainée depuis le PEB :

    typedef struct _LDR_MODULE {
    
      LIST_ENTRY              InLoadOrderModuleList;
      LIST_ENTRY              InMemoryOrderModuleList;
      LIST_ENTRY              InInitializationOrderModuleList;
      PVOID                   BaseAddress;
      PVOID                   EntryPoint;
      ULONG                   SizeOfImage;
      UNICODE_STRING          FullDllName;
      UNICODE_STRING          BaseDllName;
      ULONG                   Flags;
      SHORT                   LoadCount;
      SHORT                   TlsIndex;
      LIST_ENTRY              HashTableEntry;
      ULONG                   TimeDateStamp;
    
    } LDR_MODULE, *PLDR_MODULE;
    

    A chaque fois que tu appels FreeLibray sur une dll, ce compteur est décrémenté, quand il ateint 0 la dll est déchargée.


  • 13. martin  |  mars 13th, 2008 at 08:02

    Salut,
    Ca fonctionne aussi pour les oplocks?


  • 14. admin  |  mars 13th, 2008 at 13:25

    Je sais pas.


  • 15. 0xtceb  |  mai 23rd, 2011 at 08:27

    Supa reverse comme d’hab


Trackback this post


Calendar

juillet 2019
L Ma Me J V S D
« fév    
1234567
891011121314
15161718192021
22232425262728
293031  

Most Recent Posts