Log in

View Full Version : WIN32 Debug API


yousky
June 29th, 2004, 16:30
Hi all,

First of all, thanks to Iczelion for his tutorial on Win32 Debug API.

I've read different examples of the use of WaitForDebugEvent. I've implemented that in a little programm to handle Exception by the $CC (Int 3). All works fine except when i've rewrite the original memory code by the $CC.

Explanation:

- CreateProcess of the target
- WriteProcessMemory to put the $CC code at the right memory address for the breakpoint
- WaitForDebugEvent
- Handle the EXCEPTION_BREAKPOINT
- GetThreadContext
- WriteProcessMemory with the original 1 byte memory code at EIP - 1 to continue the execution of the target program
- SetThreadContext with the EFlags using $100
- ContinueDebugEvent
- Handle EXCEPTION_SINGLE_STEP
- WriteProcessMemory to put the $CC code at the right memory address for the breakpoint
- ContinueDebugEvent

=> The code of the target programm is executed after the breakpoint.

My problems is that after that, the WaitForDebugEvent give me EXCEPTION_ACCESS_VIOLATION so the target programm freeze and loop.

Do you have an idea for that problem ?

Thanks for your help.
Yousky

dELTA
June 29th, 2004, 17:00
What is the single stepping for? And a wild guess would be that the "right memory address for the breakpoint" is actually the wrong address anyway.

doug
June 29th, 2004, 17:37
@delta:
the single step exception handling is for the trap flag that he sets in eflags

@yousky:
could you indent the order in which you do these things. For example, are you doing a writeprocessmemory at eip-1 in the exception handler?? I don't think you should be patching to eip-1 in that case, but to eip.

Anyway, There's no magic answer to your question, you'll have to debug it yourself to see.

yousky
June 29th, 2004, 18:14
The SINGLE_STEP is to put again the breakpoint at the rigth memory address. But it seems that after my program crash with a violation access. I think this is a Memory problem when a rewrite the Int3. Dont understand for the moment why I have this violation :-(.
Move over, the EIP seems to be good, as i've rewrite the original byte code and i'm after the original breakpoint, I can in the SINGLE_STEP exception put the Int3 breakpoint again without affecting the process running.

I've check the Address memory, the old and new byte, and all seems to be ok I've dump the memory to be certain that my writeprocessmemory works.

argggg

Thanks for your reply.

Yousky

nikolatesla20
June 29th, 2004, 18:14
Don't WriteProcessMemory until after ONE INT3 comes thru first. The windows subsystem sends your debugger an INT3 exception one time, at the beginning of loading the debugee program. You must respond to this INT3 first, and then you can start doing your own thing. Otherwise, you'll get messages like "The application failed to initialize properly" or probably even that access violation you see now, since the program really isn't fully loaded yet until you tell windows you are ready by responding to that first INT3 exception.

Example:

Code:

// Wait until child process exits.
for(;
{

// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.

WaitForDebugEvent(&DebugEv, INFINITE);

// Process the debugging event code.

dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

hCurrentThread = ThreadMap[DebugEv.dwThreadId];

switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.

switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{


// This breakpoint is called once always.
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.


if (DoneOnce == FALSE)
{
// First time thru, breakpoint is from windows system


// Set up our CreateProcessW breakpoint
CreateProcessAddress = LookupAPIAddress("CreateProcessW","Kernel32.dll";

// Here you can set a breakpoint..
PlaceBreakPoint(pi.hProcess,CreateProcessAddress,CreateProcessHandler);


dwContinueStatus = DBG_CONTINUE;
DoneOnce = TRUE;

// Remember to get out here !
break;

}


if (DoneOnce == TRUE)
{

// pass unexpected int3 exception back to program
// Here you could also just handle your own INT3's that you've set.

dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

break;

}



-nt20

yousky
June 29th, 2004, 18:20
Yes I use EIP-1 with my writeprocessmemory but only in the EXCEPTION_BREAKPOINT, for the EXCEPTION_SINGLE_STEP I use the SAME EIP-1 value to put new breakpoint, not the current thread context EIP, normal.

Arggg crazy , it seems so easy on the paper lol hehe.

Thanks
Yousky

yousky
June 29th, 2004, 18:42
Here is ma Delphi code yes hehe.

-----------------------------------------------------------
// Only for the example.
OrgCode := $55;
Int3Code := $CC;
PBPAddress := Pointer($00478BD0);

// Create debug process.
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
if CreateProcess(nil, 'G:\Divers\Developpement\Projets Delphi\Swf7\Project1.exe',
nil, nil, False, CREATE_SUSPENDED + DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS,
nil, nil, StartupInfo, ProcessInfo) then
begin
// Put breakpoint at the right memory address.
if not WriteProcessMemory(ProcessInfo.hProcess, PBPAddress, @Int3Code, 1, Nb) then
raise Exception.Create('Write memory error');

// Resume thread.
ResumeThread(ProcessInfo.hThread);

// Loop.
while not Terminated do
begin
// Wait for debug events.
WaitForDebugEvent(DebugEvent, INFINITE);

// Events handler.
case DebugEvent.dwDebugEventCode of
// Debug event.
EXCEPTION_DEBUG_EVENT:
case DebugEvent.Exception.ExceptionRecord.ExceptionCode of
// Access violation exception event.
EXCEPTION_ACCESS_VIOLATION:
if DebugEvent.Exception.dwFirstChance <> 0 then
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED)
else
Terminated := True;

// Breakpoint exception event.
EXCEPTION_BREAKPOINT:
// First time.
if DebugEvent.Exception.dwFirstChance <> 0 then
begin
// Test the good address memory breakpoint (only for example).
if Cardinal(DebugEvent.Exception.ExceptionRecord.ExceptionAddress) = BPAddress then
begin
// Get context.
Context.ContextFlags := CONTEXT_CONTROL;
GetThreadContext(ProcessInfo.hThread, Context);

// EIP - 1 for writing original byte code.
dwBPLoc := Context.Eip - 1;

// Write original byte code.
if not WriteProcessMemory(ProcessInfo.hProcess, Pointer(dwBPLoc), @OrgCode, 1, Nb) then
raise Exception.Create('Write memory error');

// For EXCEPTION_SINGLE_STEP.
Context.EFlags := Context.EFlags or $0100;

// Set context.
SetThreadContext(ProcessInfo.hThread, Context);
end;

// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);

// Single step exception event.
EXCEPTION_SINGLE_STEP:
// First time.
if DebugEvent.Exception.dwFirstChance <> 0 then
begin
// Get context.
Context.ContextFlags := CONTEXT_CONTROL;
GetThreadContext(ProcessInfo.hThread, Context);

// Restore the INT 3 instruction in the place of the BP.
if not WriteProcessMemory(ProcessInfo.hProcess, Pointer(dwBPLoc), @Int3Code, 1, Nb) then
raise Exception.Create('Write memory error');

// Set context.
SetThreadContext(ProcessInfo.hThread, Context);

// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end;

EXIT_THREAD_DEBUG_EVENT,
EXIT_PROCESS_DEBUG_EVENT:
// Terminate the loop.
Terminated := True;
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end;
end;
end;
---------------------------------------------

This code is simple to read even if you don't code in delphi. So if there is an error you will certainly catch it.

Thanks to all for your help.
Yousky

yousky
June 29th, 2004, 18:44
Argg indented code sorry.

Code:

// Only for the example.
OrgCode := $55;
Int3Code := $CC;
PBPAddress := Pointer($00478BD0);

// Create debug process.
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
if CreateProcess(nil, 'G:\Divers\Developpement\Projets Delphi\Swf7\Project1.exe',
nil, nil, False, CREATE_SUSPENDED + DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS,
nil, nil, StartupInfo, ProcessInfo) then
begin
// Put breakpoint at the right memory address.
if not WriteProcessMemory(ProcessInfo.hProcess, PBPAddress, @Int3Code, 1, Nb) then
raise Exception.Create('Write memory error');

// Resume thread.
ResumeThread(ProcessInfo.hThread);

// Loop.
while not Terminated do
begin
// Wait for debug events.
WaitForDebugEvent(DebugEvent, INFINITE);

// Events handler.
case DebugEvent.dwDebugEventCode of
// Debug event.
EXCEPTION_DEBUG_EVENT:
case DebugEvent.Exception.ExceptionRecord.ExceptionCode of
// Access violation exception event.
EXCEPTION_ACCESS_VIOLATION:
if DebugEvent.Exception.dwFirstChance <> 0 then
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED)
else
Terminated := True;

// Breakpoint exception event.
EXCEPTION_BREAKPOINT:
// First time.
if DebugEvent.Exception.dwFirstChance <> 0 then
begin
// Test the good address memory breakpoint (only for example).
if Cardinal(DebugEvent.Exception.ExceptionRecord.ExceptionAddress) = BPAddress then
begin
// Get context.
Context.ContextFlags := CONTEXT_CONTROL;
GetThreadContext(ProcessInfo.hThread, Context);

// EIP - 1 for writing original byte code.
dwBPLoc := Context.Eip - 1;

// Write original byte code.
if not WriteProcessMemory(ProcessInfo.hProcess, Pointer(dwBPLoc), @OrgCode, 1, Nb) then
raise Exception.Create('Write memory error');

// For EXCEPTION_SINGLE_STEP.
Context.EFlags := Context.EFlags or $0100;

// Set context.
SetThreadContext(ProcessInfo.hThread, Context);
end;

// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);

// Single step exception event.
EXCEPTION_SINGLE_STEP:
// First time.
if DebugEvent.Exception.dwFirstChance <> 0 then
begin
// Get context.
Context.ContextFlags := CONTEXT_CONTROL;
GetThreadContext(ProcessInfo.hThread, Context);

// Restore the INT 3 instruction in the place of the BP.
if not WriteProcessMemory(ProcessInfo.hProcess, Pointer(dwBPLoc), @Int3Code, 1, Nb) then
raise Exception.Create('Write memory error');

// Set context.
SetThreadContext(ProcessInfo.hThread, Context);

// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end;

EXIT_THREAD_DEBUG_EVENT,
EXIT_PROCESS_DEBUG_EVENT:
// Terminate the loop.
Terminated := True;
else
// Continue debug.
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE);
end;
end;
end;

disavowed
June 29th, 2004, 20:39
don't forget that after clearing the breakpoint that you hit and restoring the original byte, you need to GetContext(); context.Eip--; SetContext()

if this doesn't apply to your question, then nevermind

doug
June 29th, 2004, 21:52
yes, I agree with disavowed, and this is what I tried to show my first post...

depending on where EIP is when the exception occurs (At exception / Next instruction after exception)

correct me if I'm wrong, but I think you need to do one of these two things:


At next instruction: (EIP in context points to the instruction after the CC)
- Patch at EIP-1 & Decrement EIP after the INT3 was patched with the real byte (otherwise you are in the middle of the instruction when you return)

at exception: (EIP in context point to the CC)
- Patch at EIP & don't change EIP


Right now, it looks like you are doing a mix of these two.

dELTA
June 30th, 2004, 06:18
Quote:
@delta:
the single step exception handling is for the trap flag that he sets in eflags

Well duh. I was just suspecting that this was some strange scheme to get the next good EIP or something, and that it might be the reason for the crash, that's why I asked.

nikolatesla20
June 30th, 2004, 08:42
Once again, I repeat: The windows system will pass an INT3 to your debugger RIGHT AWAY. This INT3 isn't yours, it's the system telling you the program is loaded and ready. You should not do any writeprocessmemory until this exception comes in otherwise the behavior could be undefined !

So all you do is set a flag, on the first int3 exception that comes in (EXCEPTION_BREAKPOINT), that's the windows system breakpoint. All you need to do is continue with a DBG_CONTINUE. You can also, before continuing, now set your writeprocess breakpoint. From this point forward, after continuing from the first breakpoint, any more new exceptions will be yours, etc and everything will be normal.

This is very important! I had trouble with debuggers until I finally realized this fact and accounted for it. Take a look at the code I posted above.

-nt20

yousky
June 30th, 2004, 10:02
Thanks all, I will check all yours last answers this afternoon and make tests.

Some times (rarely) i've had no crash but when I reload the EXE for debugging, the soft freeze.

I'll will try to test my EIP (doug)

Then trap the first INT3 (system window) to put the first breakpoint and then wait for my Breakpoint Event (nikolatesla20).

Bye bye
yousky

yousky
June 30th, 2004, 17:43
Arfff i've found the problems.
First thanks to all for your anwsers, very precious informations.

I've had 2 problems, first with the EIP that I will not decrement by 1 when I put the breakpoint.
Next, thanks to nikolatesla20, the first EXCEPTION_BREAKPOINT was effectively the system windows one.
And finaly, some ContinueEvent have been homited :-(( looser i am lol.

All seems to work now, thanks thanks all

see you soon
bye bye
yousky