주식회사하제소프트 (www.hajesoft.co.kr) 강사이봉석
과정소개 윈도우응용프로그램, 윈도우서비스프로그램, 윈도우디바이스드라이버를개발하는개발자들로하여금고급디버깅기술을제공하는 윈도우디버거 (WinDBG) 사용방법을익히게하여, 고급시스템프로그래머를양성하는데있습니다 윈도우디버거 (WinDBG) 를사용하는개발자는실무에서고급시스템프로그래머가갖추어야할중요한디버깅지식을습득함과동시에시간과비용을최대한아끼는프로그래밍습관과우수한결과물을만들어낼수있습니다
강사약력 본강사 ( 이봉석 ) 는기업교육 ( 삼성첨단기술센터, LG러닝센터, MDS테크놀러지 ) 과사설교육등에서 13년강사이력을가지고있으며, 현재주식회사하제소프트대표이사를역임하고있습니다. 저서로는, 윈도우디바이스드라이버, 실전윈도우디바이스드라이버, 고급개발자들만이알고있던디바이스드라이버구조와원리, 그리고제작노하우, 실전윈도우 CE 가이드, USB 너뭐니 가있습니다. 주식회사하제소프트는 17년간업체들이필요로하는윈도우디바이스드라이버, 리눅스디바이스드라이버그리고훰웨어를개발, 제공하는업체입니다.
과정진행목차 WinDBG 환경준비 Visual Studio 2015, WDK(Windows Driver Kit), WinDBG WinDBG 심볼설정 마이크로프로세서 Calling 규약 x86 CPU 에서 C 함수호출과어셈블리어연관성분석 x64 CPU 에서 C 함수호출과어셈블리어연관성분석 WinDBG 로칼디버깅 응용프로그램, 서비스프로그램디버깅 BSOD(BlueScreen Of Death) 커널덤프파일 커널덤프파일생성설정및덤프분석 WinDBG 원격디버깅 서비스프로그램, 커널디바이스드라이버디버깅 하드웨어와관련된 WinDBG 주요명령어 PCI, USB 버스를사용하는하드웨어를다룰때, 유용한 WinDBG 확장명령어를소개합니다 WinDBG 확장모듈 확장기능을제공하는모듈구현
7 강 하제소프트 WinDBG 확장모듈 WinDBG 확장모듈을개발하여자신만의명령 어를지원하는방법을배워봅니다. COM(Component Object Model) 을이해해야합니다
1. Debugger Engine Debugger Extension 라이브러리와 Debugger 응용프로그램을개발할수있도록지원합니다 WinDbg Usbkd.dll CDB Debugger Engine (DbgEng.dll) Storagekd.dll NTSD Hidkd.dll KD DbgExts.dll
2. Debugger Engine API Debugger Engine 이제공하는 API 를살펴봅니다 유형 Client Functions Debug Engine Interfaces Callback Debug Engine Interfaces Other Debug Engine Interfaces 의미 COM Interface 를얻어옵니다 일반적인 Interface 를포함합니다 Callback 관련 Interface 를포함합니다 BreakPoint, Symbol Group 관련 Interface 를포함합니다
2.1 Client Functios 새로운 Client 객체를생성하고 COM Interface 를얻어옵니다 함수 HRESULT DebugConnect( _In_ PCSTR RemoteOptions, _In_ REFIID InterfaceId, _Out_ PVOID *Interface ); HRESULT DebugCreate( _In_ REFIID InterfaceId, _Out_ PVOID *Interface ); 의미 원격호스트에연결하고, 새로운 Client 객체를생성하고 COM Interface 를얻어옵니다 새로운 Client 객체를생성하고 COM Interface 를얻어옵니다 IDebugClient * PDEBUG_CONTROL HRESULT Hr; DebugClient; DebugControl; if ((Hr = DebugCreate( uuidof(idebugclient), (void **)&DebugClient))!= S_OK) return Hr; } Hr = DebugClient->QueryInterface( uuidof(idebugcontrol), (void **)&DebugControl);
2.2 Debug Engine Interfaces 일반적인 Interface 들을포함합니다 기본 Interface IDebugAdvanced IDebugClient IDebugControl IDebugDataSpaces IDebugRegisters IDebugSymbols IDebugSymbolObjects 관련된 Interfaces IDebugAdvanced2, IDebugAdvanced3 IDebugClient2, IDebugClient3, IDebugClient4, IDebugClient5 IDebugControl2, IDebugControl3, IDebugControl4, IDebugControl5, IDebugControl6, IDebugControl7 IDebugDataSpaces2, IDebugDataSpaces3, IDebugDataSpaces4, IDebugRegisters2 IDebugSymbols2, IDebugSymbols3 IDebugSymbolObjects2, IDebugSymbolObjects3, IDebugSymbolObjects4
2.2.1 IDebugClient 여러인터페이스를얻어오는기능을제공합니다 IDebugClient * PDEBUG_CONTROL HRESULT Hr; DebugClient; DebugControl; if ((Hr = DebugCreate( uuidof(idebugclient), (void **)&DebugClient))!= S_OK) return Hr; } if ((Hr = DebugClient ->QueryInterface( uuidof(idebugcontrol), (void **)&g_extcontrol))!= S_OK) goto Fail; } if ((Hr = DebugClient ->QueryInterface( uuidof(idebugsymbols2), (void **)&g_extsymbols))!= S_OK) goto Fail; }
2.2.2 IDebugControl 디버깅을제어하는함수를포함합니다 ( 대표적인함수만보여줍니다 ) 함수 OutputVaList OutputStackTrace Output Execute GetStackTrace Evaluate GetWindbgExtensionApis64 GetActualProcessorType. 의미 문자열을지정된포멧으로정리한뒤엔진클라이언트의 Output Callback 으로문자열을전달합니다 지정된스택프레임또는현재스택프레임을 Output Callback 으로전달합니다 문자열을엔진클라이언트의 Output Callback 으로문자열을전달합니다 지정하는디버거명령어를실행합니다 지정하는콜스택의최상위스택프레임을리턴합니다 지정하는문자열의포멧을조사합니다 WINDBG_EXTENSION_APIS64 구조체를얻습니다 디버깅하는타겟의 CPU 유형을얻습니다
2.2.3 IDebugSymbols 심볼과관련된작업을돕습니다 함수 GetSymbolTypeId GetFieldName GetFieldOffset GetConstantName GetScopeSymbolGroup SetScope. 의미 지정하는심볼을위한 TypeID 를구합니다 지정하는심볼 (TypeID) 과위치에해당하는필드이름을구합니다 지정하는심볼 (TypeID) 과위치에해당하는필드오프셋값을구합니다 지정하는값에대응되는상수이름을얻습니다 지정하는타겟에포함된심볼그룹정보를얻습니다 제공하는스택프레임, 명령실행위치, 레지스터문맥을현재문맥으로지정합니다
2.2.3.1 WDbgExts 가제공하는매크로함수 하제소프트 심볼에대응되는데이터를파싱하는작업을돕기위해서 WDbgExts.h 파일 은다양한매크로함수를제공하고있습니다. 반드시 ExtensionApis 전역변수의값을초기화해야사용할수있습니다. [ 사용하려는측 ] ExtensionApis.nSize = sizeof(extensionapis); Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis); [WDbgExts.h] 기본매크로 #define dprintf (ExtensionApis.lpOutputRoutine) #define GetExpression (ExtensionApis.lpGetExpressionRoutine) #define CheckControlC (ExtensionApis.lpCheckControlCRoutine) #define GetContext (ExtensionApis.lpGetThreadContextRoutine) #define SetContext (ExtensionApis.lpSetThreadContextRoutine) #define Ioctl (ExtensionApis.lpIoctlRoutine) #define Disasm (ExtensionApis.lpDisasmRoutine) #define GetSymbol (ExtensionApis.lpGetSymbolRoutine) #define ReadMemory (ExtensionApis.lpReadProcessMemoryRoutine) #define WriteMemory (ExtensionApis.lpWriteProcessMemoryRoutine) #define StackTrace (ExtensionApis.lpStackTraceRoutine)
기본매크로이외에보다복잡한매크로를추가로지원하고있습니다 매크로함수 ( 주요 ) inline VOID ReadPhysical( ULONG64 address, _Out_writes_bytes_to_(size, *sizer) PVOID buf, ULONG size, _Out_ PULONG sizer ) inline VOID WritePhysical( ULONG64 address, _In_reads_bytes_(size) PVOID buf, ULONG size, _Out_ PULONG sizew ) inline VOID ReadIoSpace( ULONG address, _Out_writes_bytes_(*size) PULONG data, _Inout_ PULONG size ) inline VOID WriteIoSpace( ULONG address, ULONG data, _Inout_ PULONG size ) 의미 물리메모리의내용을읽습니다 물리메모리의내용을기록합니다 입출력포트를읽습니다 입출력포트를기록하니다 하제소프트 inline ULONG ReadPointer( ULONG64 Address, PULONG64 Pointer ) 지정하는위치의내용을포인터변수에담습니다 inline ULONG WritePointer( ULONG64 Address, ULONG64 Pointer ) 지정하는위치의내용으로포인터변수의값을 기록합니다 inline ULONG GetTypeSize ( IN LPCSTR Type ) 심볼의바이트크기를알려줍니다 inline ULONG GetFieldData ( _In_ ULONG64 TypeAddress, _In_ LPCSTR Type, _In_ LPCSTR Field, _In_ ULONG OutSize, _Out_writes_bytes_(OutSize) PVOID poutvalue ) 메모리의내용을심볼에맞춘뒤지정하는필드의값을얻습니다
메모리의내용을특정심볼 ( 구조체 ) 에연결하고필드의값을가져오는예재입니다 [ 정의된심볼 ] typedef struct _EXCEPTION_RECORD NTSTATUS ExceptionCode; ULONG ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; ULONG NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; [ 심볼을사용해서필드의값을가져오는방법 ] GetFieldValue(Address, "_EXCEPTION_RECORD", "ExceptionAddress", ExceptionAddress); Address 는심볼을연결하려는가상메모리주소입니다 /
중복된명령어가없다면,!usbhelp 라고줄여서사용할수있습니다 하제소프트 3. DbgEng Extension Debugger 가사용할수있도록확장기능을제공하는모듈을의미합니다 Debuggger 에서!XXXX.YYYY 형태로사용합니다 (XXXX 모듈이름, YYYY 명령어 ) 명령어 Command.load 지정하는 DbgEng Extension DLL 을로드합니다 Usbkd 모듈을로드합니다 Usbkd 모듈이제공하는명령어 (usbhelp) 를사용합니다
WinDBG 프로그램 하제소프트 DbgEng Extension 예시 출력 Usbkd.dll 입력
3.1 필수 Export 함수 DbgEng Extension 이제공하는필수 Export 함수를다음과같습니다 Export 함수 extern "C HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags) extern "C void CALLBACK DebugExtensionNotify(ULONG Notify, ULONG64 Argument) extern "C void CALLBACK DebugExtensionUninitialize(void) extern "C HRESULT CALLBACK KnownStructOutput( in ULONG Flag, in ULONG64 Address, in PSTR StructName, out_ecount(buffersize) PSTR Buffer, in PULONG BufferSize ) extern "C HRESULT _EFN_Analyze( in PDEBUG_CLIENT4 Client, in FA_EXTENSION_PLUGIN_PHASE CallPhase, in PDEBUG_FAILURE_ANALYSIS2 panalysis ) 의미 메모리에로드될때호출됩니다 정해진사건들이발생될때호출됩니다 메모리에서해제될때호출됩니다 해석할수있는심볼 ( 스트럭쳐 ) 정보를제공하고해석합니다 덤프분석용도로사용되는특별한함수입니다!Analyze 명령어와함께사용됩니다
3.2 선택 Export 함수 ( 명령어 ) DbgEng Extension 이제공하는선택 Export 함수는명령어입니다 하제소프트 WinDBG 프로그램 ( 입력창 )!XXXX.Function1 WinDBG 프로그램 ( 출력창 ) DbgEng 호출 출력 해석및출력 DbgEng Extension DLL extern "C HRESULT CALLBACK Function ( PDEBUG_CLIENT4 Client, PCSTR args) DebugControl->Output( ); DebugControl->Excute( ); }
3.3 Custom Analysis Debugger Extension 하제소프트 WinDBG 가사용하는!Analyze 명령어를도와주는확장모듈입니다 _EFN_Analyze 함수를 Export 해야합니다 Metafile 을제공해야합니다. 이파일은 Extension DLL 과같은파일이름을사용하고확장자를 ALZ 를사용합니다 MyPlugin.ALZ PluginId MyPlugin DebuggeeClass Kernel BugCheckCode 0xA BugCheckCode 0xE2 분석을지원하는 BugCheck 코드를기록합니다 WinDBG 프로그램 ( 입력창 )!Analyze -v 호출 DbgEng 로드 1 2 Custom Analysis Debugger Extension extern "C" HRESULT _EFN_Analyze( in PDEBUG_CLIENT4 Client, in FA_EXTENSION_PLUGIN_PHASE CallPhase, in PDEBUG_FAILURE_ANALYSIS2 panalysis ).. }
3.3.1 예시코드 #include <windows.h> #define KDEXT_64BIT // 64 비트환경을지원하는경우에사용합니다 #include <wdbgexts.h> #include <dbgeng.h> #include <extsfns.h> 하제소프트 extern "C" declspec(dllexport) HRESULT _EFN_Analyze(_In_ PDEBUG_CLIENT4 Client, _In_ FA_EXTENSION_PLUGIN_PHASE CallPhase, _In_ PDEBUG_FAILURE_ANALYSIS2 panalysis) HRESULT hr = E_FAIL; PDEBUG_CONTROL pcontrol = NULL; hr = Client->QueryInterface( uuidof(idebugcontrol), (void**)&pcontrol); if(s_ok == hr && NULL!= pcontrol) IDebugFAEntryTags* ptags = NULL; panalysis->getdebugfatagcontrol(&ptags); if(null!= ptags) if(fa_plugin_initilization == CallPhase) pcontrol->output(debug_output_normal, "My analyzer: initialization\n"); }
extern "C" declspec(dllexport) HRESULT _EFN_Analyze(_In_ PDEBUG_CLIENT4 Client, _In_ FA_EXTENSION_PLUGIN_PHASE CallPhase, _In_ PDEBUG_FAILURE_ANALYSIS2 panalysis) else if(fa_plugin_stack_analysis == CallPhase) pcontrol->output(debug_output_normal, "My analyzer: stack analysis\n"); } else if(fa_plugin_pre_bucketing == CallPhase) pcontrol->output(debug_output_normal, "My analyzer: prebucketing\n"); } else if(fa_plugin_post_bucketing == CallPhase) pcontrol->output(debug_output_normal, "My analyzer: post bucketing\n"); FA_ENTRY_TYPE entrytype = ptags->gettype(debug_flr_bugcheck_code); pcontrol->output(debug_output_normal, "The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x%x.\n\n", entrytype); } } } pcontrol->release(); } return hr;
3.3.2 출력 하제소프트 * Bugcheck Analysis * * * ******************************************************************************* Use!analyze -v to get detailed debugging information. BugCheck E2, 0, 0, 0, 0} My analyzer: initialization My analyzer: stack analysis My analyzer: prebucketing My analyzer: post bucketing The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x1.
예시코드는마이크로소프트가제공하는샘플예재 (dbgexts) 를사용했습니다 하제소프트 4. DbgEng Extension 예재 간단한기능을가지는 DbgEng Extension DLL 모듈을만들어봅니다 4 개의명령어를지원합니다 Export 함수 HRESULT CALLBACK cmdsample (PDEBUG_CLIENT4 Client, PCSTR args) HRESULT CALLBACK structsample (PDEBUG_CLIENT4 Client, PCSTR args) HRESULT CALLBACK help (PDEBUG_CLIENT4 Client, PCSTR args) 의미 lm 명령을사용하고, 사용자의입력을받아서실행합니다 주어진메모리를스트럭쳐에입혀서출력합니다 명령사용방법을보여줍니다 WinDBG 가설치된경로아래에 Extension 모듈경로로복사해야합니다 C:\Program Files (x86)\windows Kits\10\Debuggers\x86\winext
_EXCEPTION_RECORD 구조체에대한심볼이있는환경이어야합니다!! 하제소프트 1: kd>!dbgexts.help Help for dbgexts.dll cmdsample - This does stacktrace and lists help = Shows this help structsample <addr> - This dumps a struct at given address 1: kd>!dbgexts.structsample c0000000 c0000000: 00000000 00000000 00000000 00000000 Method 1: _EXCEPTION_RECORD @ c0000000 ExceptionCode : 0 ExceptionAddress : 00000000 ExceptionInformation[1] : 0 Method 2: _EXCEPTION_RECORD @ c0000000 ExceptionCode : 0 ExceptionAddress : 00000000 ExceptionInformation[1] : 0 Fields of _EXCEPTION_RECORD 0 (+000) ExceptionCode 1 (+004) ExceptionFlags 2 (+008) ExceptionRecord 3 (+00c) ExceptionAddress 4 (+010) NumberParameters 5 (+014) ExceptionInformation Testenum 0 == _EXCEPTION_RECORD 4.1 출력 임의의주소 0xC0000000 을사용했습니다 typedef struct _EXCEPTION_RECORD NTSTATUS ExceptionCode; ULONG ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; ULONG NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD;
4.2 샘플소스 DbgEng Extension 샘플코드는하제소프트홈페이지에서받으실수있습니다 ( 마이크로소프트가제공하는예재를사용했습니다 ) 샘플코드는 Visual Studio 2015 에서빌드할수있도록수정하였습니다
주식회사하제소프트 (www.hajesoft.co.kr) 강사이봉석