lundi 21 janvier 2008

Les apis de debugs, c'est plus fort que toi.

Bonsoir à vous,
c'est avec un peu de retard que je vous poste ce petit article de rien du tout.
Ne vous attendez pas à quelque chose d'exceptionnel, il ne s'agit juste d'une introduction aux apis de debugs.
Vous vous êtes jamais demandé, comment des désassembleurs/débuggeur tel que OllyDbg, fonctionnent-ils?
Bon bah justement c'est avec cette batterie d'apis qu'ils fonctionnent.
Je vais alors, vous expliquez le déroulement et l'utilisation de tels apis.

Tous d'abord, nous avons deux choix. En effet, soit l'on attache notre processus debuggé à notre programme (qui sera en faite le 'débuggeur') ; ou l'on
va créer directement notre processus (CreateProcess) avec les flags DEBUG_PROCESS ainsi que DEBUG_ONLY_THIS_PROCESS (afin de recevoir seulement les débugs évents relatif à notre processus et non à tous ses processus fils et co).
Une fois l'une de ces actions réalisée, notre programme devient le débuggeur
C'est à dire que grâce à la fonction WaitForDebugEvent, nous allons pouvoir stopper le ou les threads lorsqu'un 'debug évent' va être envoyé par le processus debuggé.
La liste des debugs évents est disponible ici : http://msdn2.microsoft.com/en-us/library/ms679308(VS.85).aspx.
Une fois le processus stoppé, libre à vous de modifier son contexte, à savoir ces registres ou encore de passer en mode 'single step', autrement dit 'pas à pas'.
Ceci dit, une fois fait vos petites magouilles, on relance l'attende de debug évents avec ContinueDebugEvent.
Voilà le fonctionnement du débuggeur.

Vous devez savoir aussi, que si vous avez des opérations de réécriture sur des registres, vous devez spécifié le flag CONTEXT_CONTROL dans le membre ContextFlags de la structure CONTEXT.
Si vous vous demandez de quoi est composé, la structure CONTEXT, la msdn me direz-vous, mais justement elle ne présente pas la structure c'est pour cela que je vous donne la déclaration tel quel, présente sur mon système.

typedef struct _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;


Je voulais vous parlez aussi de la réalisation du mode 'singe step'.
Celui-ci se réalise tout simplement en spécifiant un flag dans le membre EFlags, on appelle ce flag le 'Trap Bit'.
Grosso modo, vous allez faire un GetThreadContext, spécifiez le flag dans le bon membre, SetThreadContext et ContinueDebugEvent avec le flag DBG_CONTINUE.
Ensuite, à la prochaine instruction un nouveau debug évent va faire son apparition : EXCEPTION_SINGLE_STEP, si vous voulez continuer en mode pas à pas, vous re-spécifiez le flag et ainsi de suite.
Voilà pour ce qui est de la théorie, place aux screenshots :

Trouver l'adresse de la fonction à appeler :



Rewrite de l'eip



Et maintenant les codes :

DebugSingleStep.c.
RewriteEIP.c.
Cible.c.

A présent, vous êtes apte à patcher un crackme en mémoire par exemple :)).
Plus serieusement, l'outil "iinjj" developpé par Baboon utilise en partie ces apis, allez faire un tour sur son blog : http://baboon.ringzero.fr/.
Voilà en espérant que ça pourra servir à quelques d'entres vous, sur ce bonne soirée et désolé du retard.

PS: Vous pouvez aussi biensure posez vos breakpoints, en modifiant la mémoire avec WriteProcessMemory, et y posez une int 3, libre à votre immagination.

Aucun commentaire: