invoke messagebox, 0, addr searchfail, addr appname,mb_ok+mb_iconerror .endif
invoke exitprocess, 0
end start
;--------------------------------------------------------------------
; the partial source code of win.asm, our debuggee. it's actually
; the simple window example in tutorial 2 with an infinite loop inserted
; just before it enters the message loop.
;----------------------------------------------------------------------
......
mov wc.hiconsm,eax
invoke loadcursor,null,idc_arrow
mov wc.hcursor,eax
invoke registerclassex, addr wc
invoke createwindowex,null,addr classname,addr appname,\ ws_overlappedwindow,cw_usedefault,\ cw_usedefault,cw_usedefault,cw_usedefault,null,null,\ hinst,null
mov hwnd,eax
jmp $ <---- here's our infinite loop. it assembles to eb fe
invoke showwindow, hwnd,sw_shownormal
invoke updatewindow, hwnd
.while true
invoke getmessage, addr msg,null,0,0
.break .if (!eax)
invoke translatemessage, addr msg
invoke dispatchmessage, addr msg
.endw
mov eax,msg.wparam
ret
winmain endp
analysis:
invoke findwindow, addr classname, null
our program needs to attach itself to the debuggee with debugactiveprocess which requires the process id of the debuggee. we can obtain the process id by calling getwindowthreadprocessid which in turn needs the window handle as its parameter. so we need to obtain the window handle first.
with findwindow, we can specify the name of the window class we need. it returns the handle to the window created by that window class. if it returns null, no window of that class is present.
.if eax!=null
invoke getwindowthreadprocessid, eax, addr processid
mov threadid, eax
invoke debugactiveprocess, processid
after we obtain the process id, we can call debugactiveprocess. then we enter the debug loop waiting for the debug events.
.if dbevent.dwdebugeventcode==create_process_debug_event
mov context.contextflags, context_control
invoke getthreadcontext,dbevent.u.createprocessinfo.hthread, addr context
when we get create_process_debug_info, it means the debuggee is suspended, ready for us to do surgery upon its process. in this example, we will overwrite the infinite loop instruction in the debuggee (0ebh 0feh) with nops ( 90h 90h).
first, we need to obtain the address of the instruction. since the debuggee is already in the loop by the time our program attached to it, eip will always point to the instruction. all we need to do is obtain the value of eip. we use getthreadcontext to achieve that goal. we set the contextflags member to context_control so as to tell getthreadcontext that we want it to fill the "control" register members of the context structure.
invoke writeprocessmemory, dbevent.u.createprocessinfo.hprocess, context.regeip ,addr buffer, 2, null
now that we get the value of eip, we can call writeprocessmemory to overwrite the "jmp $" instruction with nops, thus effectively help the debuggee exit the infinite loop. after that we display the message to the user and then call continuedebugevent to resume the debuggee. since the "jmp $" instruction is overwritten by nops, the debuggee will be able to continue with showing its window and enter the message loop. the evidence is we will see its window on screen.
the other example uses a slightly different approach to break the debuggee out of the infinite loop.
.......
.......
.if dbevent.dwdebugeventcode==create_process_debug_event
mov context.contextflags, context_control
invoke getthreadcontext,dbevent.u.createprocessinfo.hthread, addr context
add context.regeip,2
invoke setthreadcontext,dbevent.u.createprocessinfo.hthread, addr context
invoke messagebox, 0, addr loopskipped, addr appname, mb_ok+mb_iconinformation
.......