====== Debugger ======
프로그램의 에러나 구조를 파악하기 위한 프로그램.
===== 종류 =====
==== White-box debugger ====
소스 코드를 확인할 수 있다. 대부분의 IDE가 제공하는 디버거가 이에 속한다.
==== Black-box debugger ====
소스 코드를 확인할 수 없으며 디어셈블된 결과만 확인 가능하다. Reversing Engineering할 때 쓰는 툴들이 대부분 이에 속한다. Black-box debugger는 프로그램 제어 권한 정도에 따라 다음 두 가지로 나뉜다.
=== User mode ===
최소한의 제어 권한을 가진다.
=== Kernel mode ===
운영 체제의 핵심 기능, 드라이버 등의 low-level component를 다룰 수 있다.
===== 디버그 이벤트 =====
디버거는 디버그 이벤트가 발생할 때까지 무한정 기다리게 된다. 대표적인 디버그 이벤트는 다음과 같다.
* Breakpoint hits
* Memory violations
* 디버깅한 프로그램에 의한 exceptions
디버그 이벤트가 발생하면 이에 대응되는 이벤트 핸들러가 호출된다.
===== Breakpoint =====
Breakpoint를 사용하여 프로세스를 정지시킬 수 있다. Breakpoint는 방법에 따라 크게 세 가지로 분류할 수 있다.
==== Software 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 체크섬을 사용하는 경우 이 값을 변경시킬 수 있다.
==== Hardware breakpoint ====
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를 걸 수 있다는게 단점.
==== Memory breakpoint ====
메모리의 특정 영역(페이지)에 대한 권한을 변경하는 방법이다. 메모리 페이지의 권한에 대한 몇몇 예를 들면 다음과 같다.
^권한 ^ 의미 ^
|Page execution | 실행을 가능하게 한다. 프로세스가 read/write하려 하면 access violation을 발생시킨다. |
|Page read | Page에 대한 읽기만 가능하게 한다. Write/execution하는 경우 access violation을 발생시킨다.|
|Page write | Page에 대한 쓰기를 가능하게 한다. |
|Guard page | Page에 대한 어떤 접근도 못하게 한다. 접근하는 경우 예외를 발생시키고 page를 원래 상태로 돌린다.|
위 권한을 조합하여 지정할 수도 있다. 위 권한 중 gaurd page가 reversing engineering에 유용하다.
===== 관련 윈도우 API =====
==== 프로세스 ====
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 \\
==== Breakpoint ====
=== Soft Breakpoints ===
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 \\
=== Memory Breakpoints ===
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