프로그램의 에러나 구조를 파악하기 위한 프로그램.
소스 코드를 확인할 수 있다. 대부분의 IDE가 제공하는 디버거가 이에 속한다.
소스 코드를 확인할 수 없으며 디어셈블된 결과만 확인 가능하다. Reversing Engineering할 때 쓰는 툴들이 대부분 이에 속한다. Black-box debugger는 프로그램 제어 권한 정도에 따라 다음 두 가지로 나뉜다.
최소한의 제어 권한을 가진다.
운영 체제의 핵심 기능, 드라이버 등의 low-level component를 다룰 수 있다.
디버거는 디버그 이벤트가 발생할 때까지 무한정 기다리게 된다. 대표적인 디버그 이벤트는 다음과 같다.
디버그 이벤트가 발생하면 이에 대응되는 이벤트 핸들러가 호출된다.
Breakpoint를 사용하여 프로세스를 정지시킬 수 있다. Breakpoint는 방법에 따라 크게 세 가지로 분류할 수 있다.
CPU가 실행하는 기계어 명령인 opcode(Operation Code)에 interrupt 3 (INT 3) instruction을 삽입하는 방식이다. 예를 들면 다음과 같다.
Breakpoint 설정 전 opcode
0x44332211: 8BC3 MOV EAX, EBX
Breakpoint 설정 후 opcode
0x44332211: CCC3 MOV EAX, EBX
단, 메모리에 로드된 실행 프로그램의 바이너리 내용을 변경하는 것이므로 CRC 체크섬을 사용하는 경우 이 값을 변경시킬 수 있다.
CPU의 debug register(DR0 ~ DR7)를 이용하는 breakpoint이다. 프로그램을 변경할 수 없으며 breakpoint가 적게 필요한 경우에 사용할 수 있다. Debug register의 각 역할은 다음과 같다.
Debug register | 역할 |
---|---|
DR0 ~ DR3 | Breakpoint의 주소 저장 |
DR4, DR5 | Reserved |
DR6 | 디버그 이벤트의 type 결정 |
DR7 | Breakpoint on/off, flag, length 결정 |
4개의 주소, 최대 4바이트에 대해서만 break point를 걸 수 있다는게 단점.
메모리의 특정 영역(페이지)에 대한 권한을 변경하는 방법이다. 메모리 페이지의 권한에 대한 몇몇 예를 들면 다음과 같다.
권한 | 의미 |
---|---|
Page execution | 실행을 가능하게 한다. 프로세스가 read/write하려 하면 access violation을 발생시킨다. |
Page read | Page에 대한 읽기만 가능하게 한다. Write/execution하는 경우 access violation을 발생시킨다. |
Page write | Page에 대한 쓰기를 가능하게 한다. |
Guard page | Page에 대한 어떤 접근도 못하게 한다. 접근하는 경우 예외를 발생시키고 page를 원래 상태로 돌린다. |
위 권한을 조합하여 지정할 수도 있다. 위 권한 중 gaurd page가 reversing engineering에 유용하다.
BOOL WINAPI CreateProcessA( LPCSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
프로세스를 실행시킨다. lpApplicationName, lpCommandLine, dwCreationFlags, lpStartupInfo, and lpProcessInformation 정도가 중요한 파라메터이다. 그 외 파라메터는 보통 NULL로 주면 된다. STARTUPINFO, PROCESS_INFORMATION 구조체에 대한 내용은 MSDN을 참고한다.
CreateProcess : http://msdn.microsoft.com/en-us/library/ms682425.aspx
STARTUPINFO : http://msdn.microsoft.com/en-us/library/ms686331.aspx
PROCESS_INFORMATION : http://msdn.microsoft.com/en-us/library/ms686331.aspx
HANDLE WINAPI OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle DWORD dwProcessId );
프로세스를 연 후 핸들을 리턴한다. 특정 프로세스의 디버깅을 위해 이 핸들을 사용하는 경우가 많다. 디버깅을 위해선 dwDesiredAccess는 PROCESS_ALL_ACCESS, bInheritHandle는 FALSE로 설정하면 된다. dwProcessId는 프로세스의 PID값을 넣어주면 된다. 이는 작업 관리자를 통해 확인할 수 있다.
OpenProcess : http://msdn.microsoft.com/en-us/library/ms684320.aspx
BOOL WINAPI DebugActiveProcess( DWORD dwProcessId );
프로세스를 디버거에 attach한다. Attach가 되면 디버거가 디버그 이벤트를 제어할 수 있게 된다.
DebugActiveProcess : http://msdn.microsoft.com/en-us/library/ms679295.aspx
BOOL WINAPI WaitForDebugEvent( LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds );
typedef struct DEBUG_EVENT { DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union { EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo; }u; };
디버그 이벤트를 정해진 시간만큼 기다린다. dwMilliseconds를 INFINITE로 설정하면 무한정 기다리게 된다. lpDebugEvent에 기다릴 이벤트에 대한 정보를 기록한다. 디버그 이벤트를 잡아내면 특정 이벤트 핸들러를 통해 원하는 동작을 할 수 있게 된다.
WaitForDebugEvent : http://msdn.microsoft.com/en-us/library/ms681423.aspx
DEBUG_EVENT : http://msdn.microsoft.com/en-us/library/ms679308.aspx
BOOL WINAPI ContinueDebugEvent( DWORD dwProcessId, DWORD dwThreadId, DWORD dwContinueStatus );
디버그 이벤트에 대한 이벤트 핸들러 동작을 수행 후 프로세스를 다시 시작하기 위한 함수이다. 앞의 두 파라메터는 WaitForDebugEvent 함수가 리턴된 후 DEBUG_EVENT 구조체의 변수 중 dwProcessId와 dwThreadId가 초기화 되면 얻을 수 있다. dwContinueStatus는 DBG_CONTINUE와 DBG_EXCEPTION_NOT_HANDLED가 올 수 있으며 각각 프로세스 실행을 계속할지 exception을 실행할지를 의미한다.
ContinueDebugEvent : http://msdn.microsoft.com/en-us/library/ms679285.aspx
HANDLE WINAPI OpenThread( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId );
OpenThread : http://msdn.microsoft.com/en-us/library/ms684335.aspx
HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
CreateToolhelp32Snapshot : http://msdn.microsoft.com/en-us/library/ms682489.aspx
BOOL WINAPI Thread32First( HANDLE hSnapshot, LPTHREADENTRY32 lpte );
typedef struct THREADENTRY32{ DWORD dwSize; DWORD cntUsage; DWORD th32ThreadID; DWORD th32OwnerProcessID; LONG tpBasePri; LONG tpDeltaPri; DWORD dwFlags; };
Thread32First : http://msdn.microsoft.com/en-us/library/ms686728.aspx
THREADENTRY32 : http://msdn.microsoft.com/en-us/library/ms686735.aspx
BOOL WINAPI GetThreadContext( HANDLE hThread, LPCONTEXT lpContext ); BOOL WINAPI SetThreadContext( HANDLE hThread, LPCONTEXT lpContext );
GetThreadContext : http://msdn.microsoft.com/en-us/library/ms679362.aspx
SetThreadContext : http://msdn.microsoft.com/en-us/library/ms680632.aspx
typedef struct CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; };
CONTEXT : http://msdn.microsoft.com/en-us/library/windows/desktop/ms679284(v=vs.85).aspx
BOOL WINAPI ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead ); BOOL WINAPI WriteProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten );
ReadProcessMemory : http://msdn.microsoft.com/en-us/library/ms680553.aspx
WriteProcessMemory : http://msdn.microsoft.com/en-us/library/ms681674.aspx
FARPROC WINAPI GetProcAddress( HMODULE hModule, LPCSTR lpProcName ); HMODULE WINAPI GetModuleHandle( LPCSTR lpModuleName );
GetProcAddress : http://msdn.microsoft.com/en-us/library/ms683212.aspx
GetModuleHandle : http://msdn.microsoft.com/en-us/library/ms683199.aspx
void WINAPI GetSystemInfo( _Out_ LPSYSTEM_INFO lpSystemInfo ); typedef struct _SYSTEM_INFO { union { DWORD dwOemId; struct { WORD wProcessorArchitecture; WORD wReserved; }; }; DWORD dwPageSize; LPVOID lpMinimumApplicationAddress; LPVOID lpMaximumApplicationAddress; DWORD_PTR dwActiveProcessorMask; DWORD dwNumberOfProcessors; DWORD dwProcessorType; DWORD dwAllocationGranularity; WORD wProcessorLevel; WORD wProcessorRevision; } SYSTEM_INFO;
GetSystemInfo : http://msdn.microsoft.com/en-us/library/ms724381.aspx
SYSTEM_INFO : http://msdn.microsoft.com/en-us/library/ms724958.aspx
SIZE_T WINAPI VirtualQuery( HANDLE hProcess, LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength ); typedef struct MEMORY_BASIC_INFORMATION{ PVOID BaseAddress; PVOID AllocationBase; DWORD AllocationProtect; SIZE_T RegionSize; DWORD State; DWORD Protect; DWORD Type; }
VirtualQueryEx : http://msdn.microsoft.com/en-us/library/aa366907.aspx
MEMORY_BASIC_INFORMATION : http://msdn.microsoft.com/en-us/library/aa366775.aspx
BOOL WINAPI VirtualProtectEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect );
VirtualProtectEx : http://msdn.microsoft.com/en-us/library/aa366899