디버거 single step 구현

Iczelion 아저씨가 설명한 디버거 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)

◀ 이전 페이지다음 페이지 ▶