2009년 09월 02일
디버거 single step 구현
---------------------------------------------------
1. ContextFlags에 CONTEXT_CONTROL을 지정하고, GetThreadContext함수를 호출, 플래그 레지스트를 얻는다.
2. CONTEXT구조체의 regFlag에 트랩 비트를 설정한다.
3. SetThreadContext함수를 호출한다.
4. 평소와 같이 디버그 이벤트를 기다린다. 디버그 대상 프로그램은 싱글스텝모드로 동작한다. 각 명령마다 EXCEPTION_DEBUG_EVENT로 받게 될때 u.Exception.pExceptionRecord.ExceptionCode의 값이 EXCEPTION_SINGLE_STEP으로 되게 된다.
5. 다음 명령을 트레이스 하고 싶다면, 트랩 비트를 다시 설정한다.
---------------------------------------------------
그런데 저는 안되거든여?? 뭐가 잘못된 걸까요?
아래소스에서 방법1, 방법2로 시도해 보았습니다.
동작목표는 자식프로그램이 명령어 한줄씩 실행되는 거에요.
방법1)
자식 프로세스를 생성하고 디버거 루프를 돌다가 로더브레이크가 발생하면
자식 프로세스의 첫 명령어 주소에 브레이크를 겁니다. 이후 브레이크 예외가 나오면
브레이크해제, 계속 싱글스텝을 반복 설정하는 방식입니다.
방법2)
로더브레이크가 발생하면 싱글스텝만 계속 반복 설정하는 방식입니다.
둘다 원하는 방식으로 동작하지 않는데 왜 그런지 아시는분 알려주세요..
// ==== 디버거 루프 ================================================
while(TRUE == bContinue)
{
BOOL bProcessDbgEvent = WaitForDebugEvent(&stDE,100);
switch(stDE.dwDebugEventCode)
{
case CREATE_PROCESS_DEBUG_EVENT: // 프로세스 생성
printf("\n<< Single Step Excption >>\n");
// 현재 상태 저장.
debugee.hProcess = stDE.u.CreateProcessInfo.hProcess;
debugee.hThread = stDE.u.CreateProcessInfo.hThread;
debugee.context.ContextFlags = CONTEXT_FULL;
break;
case EXCEPTION_DEBUG_EVENT: // 예외 처리
switch(stDE.u.Exception.ExceptionRecord.ExceptionCode) // 어떤 종류의 예외인가?
{
case EXCEPTION_SINGLE_STEP: // < Sigle Step 예외 >
printf("\n<< Single Step Excption >>\n");
ReadMemory(debugee.hProcess,(void*)debugee.context.Eip);
GetThreadContext(debugee.hThread,&debugee.context);
DisplayContext(&debugee.context);
// Sigle Step 세팅
if(setSingleStep==true)
SetSingleStep(&debugee);
break;
case EXCEPTION_BREAKPOINT: // < 브레이크 포인트 예외 >
if(isLoaderBreak==true) // [ 로더 브레이크 포인트 ]
{
printf("\n<< Loder Break Point >>");
isLoaderBreak=false;
/*
addr=(DWORD)stDE.u.CreateProcessInfo.lpBaseOfImage;
addr2=(DWORD)debugee.context.Eip;
문제1) 브레이크포인터 설정
if(SetBreakpoint (&debugee,(LPVOID)addr,&pOpCode))
printf("break point\n");
else
printf("fail break point\n");
*/
//문제2) 싱슬스텝 설정
SetSingleStep(&debugee);
} else { // [ 브레이크 포인트 ]
printf("\n<< Break Point >>\n");
ReadMemory(debugee.hProcess,(void*)debugee.context.Eip);
GetThreadContext(debugee.hThread,&debugee.context);
DisplayContext(&debugee.context);
ClearBreakpoint (&debugee,(LPVOID)addr,pOpCode,TRUE);
if(setSingleStep==true)
SetSingleStep(&debugee);
}
break;
}
}
ContinueDebugEvent(stDE.dwProcessId,stDE.dwThreadId,DBG_CONTINUE);
}
// Break Point 설정
BOOL SetBreakpoint(PDEBUGPACKET dp, LPCVOID ulAddr, OPCODE *pOpCode)
{
DWORD dwReadWrite = 0;
BYTE bTempOp = BREAK_OPCODE;
BOOL bReadMem;
BOOL bWriteMem;
BOOL bFlush;
MEMORY_BASIC_INFORMATION mbi;
DWORD dwOldProtect;
if ((TRUE == IsBadReadPtr(dp, sizeof(DEBUGPACKET)))||
(TRUE == IsBadWritePtr(pOpCode, sizeof(OPCODE))))
{
printf("wrong addr\n");
return FALSE;
}
bReadMem = ReadProcessMemory(dp->hProcess, (LPCVOID)ulAddr, &bTempOp,
sizeof(BYTE), &dwReadWrite);
if ((FALSE == bReadMem)||(sizeof(BYTE) != dwReadWrite)){
printf("no read/write\n");
return FALSE;
}
if (BREAK_OPCODE == bTempOp)
return -1;
VirtualQueryEx(dp->hProcess, (LPCVOID)ulAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
if (FALSE == VirtualProtectEx(dp->hProcess, mbi.BaseAddress, mbi.RegionSize,
PAGE_EXECUTE_READWRITE, &mbi.Protect))
{
printf("wrong protect\n");
return ( FALSE ) ;
}
*pOpCode = (void*)bTempOp; // 이전 op코드를 저장
bTempOp = BREAK_OPCODE; // 새로운 op코드 ( break Point )
dwReadWrite = 0; // 읽거나 쓴 바이트 수
// BreakPoint 설정.
bWriteMem = WriteProcessMemory(dp->hProcess, (LPVOID)ulAddr, (LPVOID)&bTempOp,
sizeof ( BYTE ), &dwReadWrite);
if ((FALSE == bWriteMem)||(sizeof(BYTE) != dwReadWrite))
return FALSE;
VirtualProtectEx(dp->hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);
bFlush = FlushInstructionCache(dp->hProcess, (LPCVOID)ulAddr, sizeof(BYTE)) ;
printf("addr: %x\n",ulAddr);
return TRUE;
}
BOOL ClearBreakpoint ( PDEBUGPACKET dp, LPCVOID ulAddr, OPCODE OpCode, BOOL bChangeRegs)
{
BYTE bTempOp = (BYTE)OpCode;
DWORD dwWrite = 0;
BOOL bWriteMem;
BOOL bFlush;
MEMORY_BASIC_INFORMATION mbi;
DWORD dwOldProtect;
if (TRUE == IsBadReadPtr(dp,sizeof(DEBUGPACKET)))
return FALSE;
VirtualQueryEx(dp->hProcess, (LPCVOID)ulAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
if (FALSE == VirtualProtectEx(dp->hProcess, mbi.BaseAddress, mbi.RegionSize,
PAGE_EXECUTE_READWRITE, &mbi.Protect))
{
return FALSE;
}
// 이전 Opcode로 되돌려 놓음.
bWriteMem = WriteProcessMemory(dp->hProcess,(LPVOID)ulAddr,(LPVOID)&bTempOp,
sizeof(BYTE),&dwWrite);
if ((FALSE==bWriteMem)||(0==dwWrite))
return ( FALSE ) ;
// 메모리 보호 상태를 원래대로 되돌림.
VirtualProtectEx(dp->hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect,&dwOldProtect);
// CPU 명령어 캐쉬를 비움.
bFlush = FlushInstructionCache(dp->hProcess, (LPCVOID)ulAddr, sizeof(BYTE)) ;
if (TRUE==bChangeRegs)
{
// BreakPoint 부분으로 EIP를 설정.
dp->context.Eip-- ;
if (FALSE == SetThreadContext(dp->hThread,&dp->context))
return FALSE;
}
return TRUE;
}
// SingleStep Exception Setting
BOOL SetSingleStep(PDEBUGPACKET dp)
{
BOOL bSetContext ;
if ( TRUE == IsBadReadPtr (dp, sizeof(DEBUGPACKET)))
return FALSE;
// <<i386>> TF bit를 설정하여 SigleStep을 활성화.
dp->context.EFlags |= TF_BIT ;
bSetContext = SetThreadContext(dp->hThread,&dp->context);
return bSetContext ;
}
// SingleStep Exception Clear
BOOL ClearSingleStep(PDEBUGPACKET dp)
{
BOOL bSetContext ;
if ( TRUE==IsBadReadPtr(dp,sizeof ( DEBUGPACKET)))
return ( FALSE ) ;
// <<i386>> TF bit를 설정하여 SigleStep을 비활성화.
dp->context.EFlags &= ~TF_BIT ;
bSetContext = SetThreadContext(dp->hThread,&dp->context);
return bSetContext;
}
# by | 2009/09/02 12:58 | 트랙백 | 덧글(1)



