Dans ce tutorial, nous allons continuer l'exploration de debug win32 api. Et spécialement, nous allons voir comment tracer le debuggee.
Downloadez l'exemple.
Si vous avez déjà utilisé un débugger auparavant, vous êtes familiers avec le fait de tracer un autre programme grâce à lui. Quand vous "tracez" un programme, les Break Points sur n'importe quelle instruction, vous donne la chance d'examiner les valeurs des Registres et des mémoires. Le fait de tracer un programme, on appelle ça officiellement 'Single-stepping'.
Plus concrètement, tracer un programme c'est exécuter une ligne d'instruction du programme cible puis voir ce que ça produit,( ou bien regarder les valeurs des Registres ou des mémoires qui viennent de changer) puis passer à la ligne suivante et encore voir ce qu'elle fait et ainsi de suite pour comprendre comment fonctionne le programme. Seul un debugger est capable de faire ça.
Le traçage (ou 'Single-stepping') a la particularité d'être générer pas le CPU lui-même. (Le CPU c'est l'organe de calcul de l'ordinateur, son coeur). Le 8ème bit du flag 'REGISTER' est appelé le Trap Flag. Si ce flag est mis à 1, le CPU fonctionne en mode pas à pas ou traçage, c'est pareil, enfin bon en 'Single-stepping'. Le CPU produira une exception de debug pour le stopper après chaque instruction. Après que l'exception de debug soit produite, le 'Trap Flag' est automatiquement purifié.
Nous pouvons aussi tracer le debuggee, en employant les API win32 debug. Les étapes sont les suivantes :
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\user32.lib
.data
AppName db "Win32 Debug Example no.4",0
ofn OPENFILENAME <>
FilterString db "Executable Files",0,"*.exe",0
db
"All Files",0,"*.*",0,0
ExitProc db "The debuggee exits",0Dh,0Ah
db "Total Instructions executed
: %lu",0
TotalInstruction dd 0
.data?
buffer db 512 dup(?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
context CONTEXT <>
.code
start:
mov ofn.lStructSize,SIZEOF ofn
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,512
mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER
or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke GetStartupInfo,addr startinfo
invoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE,
DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi
.while TRUE
invoke WaitForDebugEvent, addr DBEvent,
INFINITE
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
invoke wsprintf, addr
buffer, addr ExitProc, TotalInstruction
invoke MessageBox, 0,
addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION
.break
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
mov
context.ContextFlags, CONTEXT_CONTROL
invoke
GetThreadContext, pi.hThread, addr context
or
context.regFlag,100h
invoke
SetThreadContext,pi.hThread, addr context
invoke
ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP
inc
TotalInstruction
invoke
GetThreadContext,pi.hThread,addr context or context.regFlag,100h
invoke
SetThreadContext,pi.hThread, addr context
invoke
ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId,DBG_CONTINUE
.continue
.endif
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId,
DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
.endif
invoke CloseHandle,pi.hProcess
invoke CloseHandle,pi.hThread
invoke ExitProcess, 0
end start
Ce programme ouvre une boîte de dialogue openfile. Quand l'utilisateur choisit un fichier exécutable, il exécute le programme cible en mode single-step (tracage pas à pas), et il compte le numéro d'instructions exécutées jusqu'à la sortie de ce programme cible.
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
Nous en profitons pour faire fonctionner le programme cible en mode single-step. Souvenez-vous que Windows envoie un EXCEPTION_BREAKPOINT juste avant qu'il n'exécute la première instruction du prog cible.
mov context.ContextFlags, CONTEXT_CONTROL
invoke
GetThreadContext, pi.hThread, addr context
On appelle GetThreadContext pour remplir la structure CONTEXT avec les valeurs actuelles des Registres du debuggee. Plus spécifiquement, nous avons besoin de la valeur actuelle du flag REGISTER.
or context.regFlag,100h
Nous mettons le trap bit (le 8ème bit) dans l'image du flag REGISTER.
invoke SetThreadContext,pi.hThread, addr context
invoke
ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
Then we call SetThreadContext to overwrite the values in the CONTEXT structure with the new one(s) and call ContinueDebugEvent with DBG_CONTINUE flag to resume the debuggee.
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP
inc
TotalInstruction
Quand une instruction est exécutée dans le debuggee, nous recevons un EXCEPTION_DEBUG_EVENT. Nous devons examiner la valeur de u.Exception.pExceptionRecord.ExceptionCode. Si la valeur est EXCEPTION_SINGLE_step, alors cet événement de debug s'est produit à cause du mode Pas à Pas (single-step). Dans ce cas, nous pouvons incrémenter la variable TotalInstruction car nous savons qu'une seule et unique instruction vient d'être exécutée dans le programme cible.
invoke GetThreadContext,pi.hThread,addr context or context.regFlag,100h
invoke
SetThreadContext,pi.hThread, addr context
invoke
ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId,DBG_CONTINUE
.continue
Après que l'exception de debug soit arrivée on enlève (on purifie) le 'trap flag'. On doit remettre le trap flag de nouveau si on souhaite continuer en mode Pas à Pas (single-step).
Avertissement : n'employez pas l'exemple dans ce tutorial avec un grand programme : le traçage est LENT. Il vous faudrait attendre pendant dix minutes avant que vous ne puissiez fermer le debuggee.
[Iczelion's Win32 Assembly Homepage]