04. 디버거는 어떻게 작동하는가?
디버거 : 다른 프로세스를 조정할 수 있는 프로세스(parent)
디버기 : 디버거에서 시작된 프로세스(child)
Window 디버거들의 종류
: 사용자 모드 디버거(애플리케이션 디버깅), 커널모드 디버거(OS 커널 디버깅)
사용자 모드 디버거 : Win32 Debugging API 사용. 프로세스가 Debugging API에서 실행되어 디버기를 만들면 디버거는 그 프로세스에서 떨어질 수 없게 된다.
해석된 언어나 버추얼 머신 접근을 이용하는 런타임(JVM)등은 버추얼 머신 자체가 환경을 제공하기 때문에 Debugging API를 사용하지 않는다.
- WinDBG, BoundsChecker, Platform SDK Heap Walker, Platform SDK Depends, Delphi, Builder, NTSD 등
cf) IsDebuggerPresent() 프로세스가 디버거 안에서 작동되고 있는지 확인 가능
커널 모드 디버거 : CPU와 OS사이에서 작동한다. 즉, 커널 모드 디버거에서 멈추면 OS도 멈춘다.
- windows 80386 debugger, kernel debugger(i386KD), WinDBG, SoftICE
WinDBG : 커널 및 사용자 모드 디버거도 지원, 두 가지 모드 동시에 디버깅은 불가능. WinDBG는 소스레벨 디버깅을 사용하며, Command 인터페이스를 통해 GUI보다 빠르게 디버깅 할 수 있다. 비주얼 c++보다 높은 확장성을 가지고 있으며, 덤프 파일을 가지고 프로그램을 다시 시작하는 정지점 명령을 만들 수 있다.
SoftICE : 사용 커널 모드 디버거. 사용자 모드 프로그램을 디버깅 가능. 사용자 모드 프로그램 디버깅 시 OS를 완전히 멈춘 상태에서 CPU와 OS사이에서 작동. 멀티 쓰레드에서 효과적인 디버깅 가능. OS작동을 멈춤으로 타이밍 문제 해결에 용이하고, 프로세스간의 상호작용 디버깅이 가능하다. 또한 OS에서 일어나는 모든 내용을 가상으로 확인 가능.
디버거에서 자동으로 시작하기.
디버그하기 어려운 종류의 어플리케이션들은 대부분 다른 프로세스에서 시작되는 것들이다. Window2000에서는 여러분의 어플리케이션이 디버거에서 시작하도록 지정할 수 있다.
1. GFLAGS.EXE이용
2. regedit에서 옵션 설정
빠른 정지키들
디버거에 빨리들어가는 방법. 콘솔 기반 디버깅 중이라면 Ctrl+C나 Ctrl+Break를 누르면 특별한 예외인 DBG_CONTROL_C를 만들어 낸다. 이는 디버거를 바로 실행하고 디버깅을 시작할 수 있게 해준다. Window2000, NT의 경우 default로 F12를 누르면 debugBreak를 호출하게 된다.
MinDBG: 간단한 Win32 디버거
Win32 디버거는 단순한 프로그램이다. 첫 번째 사양은 디버거가 CreateProcess의 dwCreationFlags 매개변수에서 특별한 플래그(DEBUG_ONLY_THIS_PROCESS)를 거쳐야 한다는 것이다. 디버깅시 안전성을 위해 디버거와 디버기는 별도의 프로세스로 구성되어 충돌되지 않는다.
두 번째 사양은 디버기가 시작된 후 디버거는 WaitForDebugEvent API 함수를 호출하는 루프에 들어와서 디버깅 알람을 받아야 한다는 것이다. 특별한 디버깅 이벤트의 프로세스를 마치면 ContinueDebugEvent를 호출한다.
디버거가 디버그 루프에 있는 동안 디버거는 어떤 이벤트가 디버기에 위치하고 있는지 알람을 받는데, WaitForDebugEvent()로 채워진 DEBUG_EVENT 구조는 디버그 이벤트에 대한 좋은 정보를 가지고 있다.
디버깅 이벤트
CREATE_PROCESS_DEBUG_EVENT : 프로레스가 사용자모드에서 실행되기 전, 커널이 새로운 프로세스에 대한 디버깅 이벤트를 생성하기 전에 생성
CREATE_THREAD_DEBUG_EVENT : 새로운 스레드가 시작되어 사용자 모드에서 실행되기 전에 생성
EXCEPTION_DEBUG_EVENT : 디버깅 프로세스에서 예외 발생시 생성
EXIT_PROCESS_DEBUG_EVENT : 디버깅 프로세스에서 마지막 스레드 종료시 생성
EXIT_THREAD_DEBUG_EVENT : 디버깅 프로세스의 일부 스레드 종료시 마다 생성
LOAD_DLL_DEBUG_EVENT : 디버깅 프로세스가 DLL 로드시 생성
OUTPUT_DEBUG_STRING_EVENT : 디버깅 프로세스가 OoutputDebugStrin()사용시 생성
UNLOAD_DLL_DEBUG_EVENT : FreeLibrary()이용하여 DLL언로드할 때마다 생성
RIP_INFO : 디버깅 이벤트로 Win98의 체크빌드에 의해 생성되고 유효하지 않는 핸들을 닫는 것처럼 오류 조건을 보고하는데 사용
디버거가 WaitForDebugEvent에 의해 디버깅 이벤트를 수행할 때 OS가 디버기에 있는 모든 스레드를 멈추고 ContinueDebugEvent가 호출되어 리 스케쥴링하기 때문에 디버거가 디버기에 대한 전체적 통제를 가진다.
- 디버거가 디버기의 주소공간 사용시 ReadProcessMemory, WriteProcessMemory 사용
- 메모리가 읽기 전용이라면 VirtualProtect()을 통한 보호 수준 리셋
cf) WriteProcessMemery를 사용한다면 FlushInstructionCache를 호출하여 캐시를 지워야 한다.
- 디버거에서 디버기의 컨텍스트나 CPU 레지스터 사용시 GetThreadContext, SetThreadContext를 호출
- 로더 정지점 : 특별한 핸들 필요. OS가 모듈에 초기 CREATE_PROCESS_DEBUG_EVENT 에 대한 알람을 보낸 후 디버거는 EXECPTION_DEBUG_EVENT(로더 정지점)를 받는다.
로더 정지점 발생시, 디버거는 정지점을 보고하고 그에 따른 이후 정지점을 다룰 수 있다.
sample minidbg
디비거 정보 정리 사이트
WNDBG 살펴보기
- WNDBG 메인 서브 시스템
WDBG.EXE 모든 UI코드 가지며, 정지점 프로세스 관리. 대부분의 작업은 WDBGRPOJDOC.CPP에서 발생
LOCALDEBUG.DLL 디버그 루프 포함
LOCALASSIST.DLL 디버기의 메모리와 레지스터를 조작하기 위해 API함수 둘러싸고 있다.
I386CPUHELP.DLL IA32(팬티엄)helper 모듈
메모리의 읽기와 쓰기
CREATE_PROCESS_DEBUG_EVENT에서 반환된 프로세스의 핸들이 PROCESS_VM_READ, PROCESS_VM_WRITE 액서스를 가지고 있기 때문에 디버거가 엑세스를 시작하면 디버기에 대한 모든 액서스를 갖게 된다.
copy-on-write : window가 실행파일 로드시 그것을 이용하는 프로세스와 바이너리 매핑된 메모리 페이지를 공유한다. 이 프로세스 중 하나가 디버거에서 실행되고 있는 작성된 정지점을 가진다면, (모든 프로세스에서 정지점을 가지지 않으므로) 디버거 밖에서 실행되는 프로세스가 그 코드 실행시 정지점 예외로 크래시가 발생할 것이다.
이런 예외처리를 위해 private 속성을 가진 페이지를 복사한다.
디버기 메모리 작성은 읽기전용 이기 때문에 VirtualQueryEx(현재 페이지의 보호권 획득), VirtualProtectEX(API) 를 이용 PAGE_EXECUTE_READWRITE로 설정, cpoy-on-write상태로 만든다. 메모리를 쓴 후에는 원래 상태로 되돌려야 한다.
cf)Win32 Debugging API는 OUTPUT_DEBUG_STRING_EVENT가 생기면 디버거는 스트링을 출력한다. 디버그를 통과하는 정보들은 스트링의 위치와 길이를 가지고 있다.
정지점과 단일 스태핑(Line by Run)
정지점 설정 : 메모리 주소에 위치에 opcode를 저장하고 주소에 지시를 작성
0 개의 댓글:
댓글 쓰기