HideFromDebugger
mai 13th, 2007 at 06:29 admin
Une explosion ! Vite je cours me mettre à l’abri, je prends mon AK-47 et m’accroupit, respirant calmement j’attends que l’ennemi sorte de son trou, il fait chaud, une goutte de sueur dégouline le long de ma tempe, ma combinaison est un vrai fourneau, j’ai peur ! Il se montre enfin ! Enculé de batard, tu vas la prendre ta rafale, TAC TAC TAC ! Headshot, Terrorists Wins. Ouf quelle partie, ca fait du bien de buter du noob à CS, surtout après une dure journée. Vous l’avez compris en ce moment je fous plus grand chose, petite période d’idle ou j’ai envie de me changer les idées et pour vous décevoir rien de tel qu’un post sans grand-intéret. Rien de bien extraordinaire, je vais juste vous parler d’un anti-debug peu connu.
En lisant de la doc sur l’API native, je suis tombé (entre 2 parties de CS) sur une feature NtSetInformationThread, appelé ThreadHideFromDebugger. L’utilisation est simple on appel NtSetInformationThread avec le ThreadInformationClass mit à ThreadHideFromDebugger (17) et un thread handle.
typedef enum _THREADINFOCLASS { ThreadHideFromDebugger=17 } THREADINFOCLASS;extern "C" ULONG __stdcall NtSetInformationThread( __in HANDLE ThreadHandle, __in THREADINFOCLASS ThreadInformationClass, __in_bcount(ThreadInformationLength) PVOID ThreadInformation, __in ULONG ThreadInformationLength ); ULONG main() { ULONG Status; Status=NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0); if(Status) printf("Error with NtSetInformationThread : 0x%xn", Status); __asm {int 3} return 0; }
Après si on essaye de lancer le debugger sur ce thread, rien ne se passe, le breakpoint (int 3) n’est pas récupéré par le debugger. En fait toutes les exceptions ne sont plus passées au debugger :]
Pour comprendre il faut savoir que lorsque qu’on trace sous Olly, le debugger positionne le trap-flag du registre EFLAGS à 1, ainsi à chaque exécution d’instruction, le processeur va générer une exception, le débugger va la gérer et nous permettre de continuer sur la prochaine instruction et ainsi de suite. Si par malheur ces exceptions ne sont plus passées au debugger, il n’est plus possible de tracer le thread.
Ainsi dans le code plus haut, si vous lancer Olly sur l’exécutable vous ne verrez jamais le debugger vous signaler l’int 3, mais vous verrez la MessageBox « HideFromDebugger.exe a rencontré un problème et doit fermer. Nous vous prions de nous excuser pour le désagrément encouru. », du gestionnaire d’exception terminal, comme si le debugger n’avait pas été prit en compte. Dans un cas normal celui-ci break et nous propose de continuer l’exécution du programme.
Vu de haut c’est mignon comme feature, mais c’est toujours mieux de voir comment c’est fait dedans, aller hop wormhole trip avec IDA.
Voici le code qui correspond à la feature ThreadHideFromDebugger de NtSetInformationThread.
NtSetInformationThread [...] push edi ; HandleInformation lea eax, [ebp+pETHREAD] push eax ; Object push dword ptr [ebp+AccessMode] ; AccessMode push _PsThreadType ; ObjectType push 20h ; DesiredAccess push [ebp+Handle] ; Handle call _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) mov esi, eax test esi, esi jl loc_49E718 mov ecx, [ebp+pETHREAD] lea eax, [ecx+248h] ; eax=pETHREAD+0x248 lock or [eax], ebx ; ebx=4 jmp loc_49E713 [...]
Le noyau récupère un pointeur sur la struture ETHREAD de notre thread avec l’api ObReferenceObjectByHandle puis accède au champ situé en 0×248 pour effectuer un « lock or [pETHREAD+0x248], 4. Le préfixe lock permet de verrouiller le bus de données afin que dans un environnement multiprocesseur d’autres threads ne modifient pas la variable en même temps.
En 0×248 de la structure ETHREAD on trouve :
kd>dt nt!_ETHREAD [...] +0x248 CrossThreadFlags : Uint4B +0x248 Terminated : Pos 0, 1 Bit +0x248 DeadThread : Pos 1, 1 Bit +0x248 HideFromDebugger : Pos 2, 1 Bit +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit +0x248 SystemThread : Pos 4, 1 Bit +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit +0x248 BreakOnTermination : Pos 6, 1 Bit +0x248 SkipCreationMsg : Pos 7, 1 Bit +0x248 SkipTerminationMsg : Pos 8, 1 Bit [...]
En faisant un OR avec cette variable, NtSetInformationThread positionne le bit HideFromDebugger à 1. Jusqu’ici rien d’extraordinaire.
Au moment de l’exception int 3, le processeur lance la routine le de l’IDT (Interruption Descriptors Table) appelé KiTrap03 qui va ensuite appelé les fonction suivantes :
KiTrap03->CommonDispatchException->KiDispatchException->DbgkForwardException
Arrivé dans DbgkForwardException on voit :
__stdcall DbgkForwardException(x, x, x) [...] PAGE:004AB2C4 64 A1 24 01 00 00 mov eax, large fs:124h ; Current ETHREAD PAGE:004AB2CA F6 80 48 02 00 00 04 test byte ptr [eax+248h], 4 ;ETHREAD+248 & HideFromDebugger PAGE:004AB2D1 0F 85 58 7C 09 00 jnz loc_542F2F ; si HideFromDebugger enabled on ne passe pas l'except au debugger [...]
La fonction DbgkForwardException va vérifier si le champ HideFromDebugger du thread courant est à 1, si oui alors l’exception n’est pas passé au debugger. Dans une situation classique l’exception est transmise au debugger via DbgkpSendApiMessageLpc qui va notifier à l’api WaitForDebugEvent qu’une exception s’est déroulé.
Voilà, vous trouverez le code/binaire ici :
http://ivanlef0u.fr/repo/HideFromDebugger.rar
Entry Filed under: Non classé
4 Comments
1. Anonyme | mai 16th, 2007 at 10:26
Hihi. merchi :p
2. andrewl | octobre 22nd, 2010 at 08:20
sorry to raise old post, but I just encountered this trick in very expensive target and was lucky to find your page – thanks for posting!
3. waliedassar | novembre 2nd, 2012 at 19:10
Merci for this nice stuff.
There is a couple of points that i need to add for whom it may concern regarding the « NtSetInformationThread » function with the « ThreadInformationClass » parameter set to 0×11 (ThreadHideFromDebugger):
1) The « InformationLength » parameter must be zero. Any non-zero value passed in this parameter will cause the function to fail.
2) This function call seems to be one-way i.e. once you set the thread as hidden, you can’t call the function again to unhide it.
4. Noteworthy | avril 23rd, 2013 at 13:07
Salut Ivan,
J’ai un petit souci avec NtSetInformationThread / ThreadHideFromDebugger, la variable status vaut toujours NULL quelque soit l’éthat du thread debuggé ou pas.
Je tourne sur Win7 SP1 x64 virtualisé.
Voilà mon code :
http://www.dpaste.org/0pIIy/
Trackback this post