뭉근 : 느긋하게 타는 불

들어가며

그냥 MS 심볼 정보를 얻어야 될 일이 있어서 여차저차 알아보다 보니까 그 동안 모르고 넘겼던 MS 심볼 디렉터리에 생기는 폴더 이름들에 대해 알게되었다. 알고보니까 직접 배포할 때도 이런 형식을 맞춰야 하는거 같다.

   

MS 심볼 폴더 구성

MS 심볼 폴더 구성은 크게 PE 파일에 대한 폴더pdb 파일에 대한 폴더 두 가지로 나뉜다. 우선 PE 파일 폴더 구성은 아래와 같다.

   

"%s\%s\%s%s\%s" % (serverName, peName, timeStamp, imageSize, peName)

   

딱 보면 이게 무슨 말인지 아시는 분들도 있고 모르시는 분들도 있겠다. 일단 아래의 사례를 통해 이해해보도록 하겠다.

   

   

위 심볼 정보는 MS 심볼 패키지 모음에서 다운받은 Windows 8.1 심볼 패키지 중 하나이다. ntoskrnl.exe는 심볼 서버의 저장된 PE 파일이다. 경로를 그려보면

"~\WebSymbols\ntoskrnl.exe\52341CF4781000\ntoskrnl.exe" 라 할 수 있다.

여기서 serverName은 WebSymbols로 심볼 정보를 설치한 폴더 이름이고, peName은 그대로 ntoskrnl.exe이다. 그다음 timeStamp + imageSize가 섞인 것을 볼 수 있는데 이는 해당 헥스값을 문자열로 표현한 것이다. timeStamp + imageSize 폴더 하위에는 다시 peName으로 PE 파일이 있다. 이 timeStamp와 imageSize 값은 dumpbin같은 PE 도구를 확인하여 확인할 수 있다.

   

   

자동화를 한다면 하드 코딩하거나 아래 정도로 사용하면 될 거 같다.

   

   

(텍스트)

~\WebSymbols\ntoskrnl.exe\52341CF4781000>dumpbin.exe ntoskrnl.exe /headers | grep "date stamp\|size of image"

52341CF4 time date stamp Sat Sep 14 17:23:16 2013

781000 size of image

   

   

확인하면 폴더명이 [52341CF4 + 781000] 인 것을 확인할 수 있다.

   

다음으로 pdb 파일에 대한 폴더인데 이 pdb 파일은 PE 파일에 대한 디버깅 정보를 포함하는 파일이다. pdb 파일은 아래와 같은 구성을 따른다.

   

"%s\%s\%s%s\%s" % (serverPath, pdbName, guid, age, pdbName)

   

여기서 guid는 PE 파일의 Directory 중 Debug Directory에서 찾아볼 수 있으나 왠만한 PE 도구에서는 GUID까지 보여주지 않는다. VC에서 포한하는 도구인 dumpbin을 이용하여 찾아볼 수 있다.

   

   

(텍스트)

~\WebSymbols\ntoskrnl.exe\52341CF4781000>dumpbin.exe ntoskrnl.exe /headers | grep "Format:"

52341CF4 cv 25 0001AFF0 1A5F0 Format: RSDS, {FD3D00D2-8EDC-4527-BB92-2BCC0509D285}, 1, ntkrnlmp.pdb

   

RSDS 다음에 나오는 헥스 문자열과 그 뒤에 오는 숫자(여기선 1)이 바로 GUID와 age이다. 또한 PDB 파일의 이름이 ntkrnlmp.pdb라고 친절하게 나와있다. 위의 폴더 구성을 통해 파일의 위치를 찾아보면 아래와 같은 폴더를 찾을 수 있다. 여기서 age란 incremental build를 사용하여 컴파일하면 초기값 1부터 시작하여 증가한다고 한다.

   

~\WebSymbols\ntkrnlmp.pdb\FD3D00D28EDC4527BB922BCC0509D2851\ntkrnlmp.pdb

   

   

근데 왜 PE 파일과 pdb 파일명이 서로 다를까? 생각해봤지만. 음… 뭐 MS의 마음이니까 라고 결론을 내렸다. 뭐 위와 같은 심볼 폴더 구성을 알면 뭐가 좋냐 하면 PE 파일명과 PDB 파일명이 달라 디버깅하지 못하는 이런 사태를 피할 수 있다.

   

마치며

내가 하고 싶은건 모든 ntoskrnl.exe의 모든 버전에 대한 심볼 정보를 얻어오는 것인데.. :(

뭐 이런 것도 정리해 놓으면 도움이 되겠지 생각한다.

참고

<Symbols the Microsoft Way, https://randomascii.wordpress.com/2013/03/09/symbols-the-microsoft-way/>

   

2015.03.13 추가.

ntoskrnl.exe는 윈도우 업데이트 폴더(ex. WinSxS)에서 구할 수 있다. 이 폴더는 그 동안 업데이트된 파일들을 저장한다.

그러나 모든 버전의 ntoskrl.exe는 구할 수 없다. 아마도 언어마다 업데이트가 다른 것도 있어서? 인거 같다. GRR 도구 참고.

'배려'의 명언

생활2014. 11. 24. 21:12

자네는 스스로에게 솔직한 적 있었는가?

지금의 자신을 인정하지 않다 보니 자꾸 허상만을 좇게 되는 거야.

그래서 박탈감을 느끼고 남의 탓만 하는 거지.

지금 처한 현실이 어렵다면, 대가를 치르고 있다고 생각하게.

내가 선택하지 않은 인생은 없지.

모든 것은 스스로 선택한 데 따른 결과물이야.

과거의 잘못된 선택에 대한 대가를 치르는 것이지.

그걸 솔직하게 인정해야 하네.

그게 행복의 첫 번째 조건이야.

자신에게 솔직해져야 마음이 편안해지고 행복을 받아들일 준비가 되는 거야.

행복은 추구해야 할 목표가 아니야 행복은 삶의 과정에서 언제든 찾아낼 수 있는 것이지.”

 

내가 주도해서 했건 남이 주도하는 걸 따랐건 결국 내가 선택한거다. 이건 나의 책임이다. 인정하자.

Connected to Windows 8 9841 x86 compatible target at (Fri Nov 14 08:06:42.382 2014 (UTC + 9:00)), ptr64 FALSE
Kernel Debugger connection established.

************* Symbol Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       srv*D:\WebSymbols*http://msdl.microsoft.com/download/symbols
Symbol search path is: srv*D:\WebSymbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows 8 Kernel Version 9841 MP (1 procs) Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 9841.0.x86fre.fbl_release.140912-1613
Machine Name:
Kernel base = 0x81276000 PsLoadedModuleList = 0x8148d6d8
Debug session time: Fri Nov 14 08:06:36.550 2014 (UTC + 9:00)
System Uptime: 0 days 0:05:40.451
Break instruction exception - code 80000003 (first chance)

// 기존 8.1의 경우 9600임

 

kd> !pcr
KPCR for Processor 0 at 814a0000:
    Major 1 Minor 1
 NtTib.ExceptionList: 827e6f6c
     NtTib.StackBase: 00000000
    NtTib.StackLimit: 00001f80
  NtTib.SubSystemTib: 807d5000
       NtTib.Version: 0002e7cc
   NtTib.UserPointer: 00000001
       NtTib.SelfTib: 00000000

             SelfPcr: 814a0000
                Prcb: 814a0120
                Irql: 0000001f
                 IRR: 00000000
                 IDR: 00000000
       InterruptMode: 00000000
                 IDT: 807e1400
                 GDT: 807e1000
                 TSS: 807d5000

       CurrentThread: 814be200
          NextThread: 00000000
          IdleThread: 814be200

           DpcQueue: Unable to read nt!_KDPC_DATA.DpcListHead.Flink @ 814a2300

kd> dd nt!KiInitialPCR
814a0000  827e6f6c 00000000 00001f80 807d5000
814a0010  0002e7cc 00000001 00000000 814a0000
814a0020  814a0120 0000001f 00000000 00000000
814a0030  00000000 81473d40 807e1400 807e1000
814a0040  807d5000 00010001 00000001 00000d40
814a0050  00000000 00000000 00000000 00000000
814a0060  00000000 00000000 00000000 00000000
814a0070  00000000 00000000 00000000 00000000

kd> dt _KPCR 814a0000
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 Used_ExceptionList : 0x827e6f6c _EXCEPTION_REGISTRATION_RECORD
   +0x004 Used_StackBase   : (null)
   +0x008 MxCsr            : 0x1f80
   +0x00c TssCopy          : 0x807d5000 Void
   +0x010 ContextSwitches  : 0x2e7cc
   +0x014 SetMemberCopy    : 1
   +0x018 Used_Self        : (null)
   +0x01c SelfPcr          : 0x814a0000 _KPCR
   +0x020 Prcb             : 0x814a0120 _KPRCB
   +0x024 Irql             : 0x1f ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0
   +0x034 KdVersionBlock   : 0x81473d40 Void
   +0x038 IDT              : 0x807e1400 _KIDTENTRY
   +0x03c GDT              : 0x807e1000 _KGDTENTRY
   +0x040 TSS              : 0x807d5000 _KTSS
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0xd40
   +0x050 SpareUnused      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0x1000000
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB

 

// KPCR 크기는 역시나 계속 동일함


kd> dt _KPRCB 814a0000+120
nt!_KPRCB
   +0x000 MinorVersion     : 1
   +0x002 MajorVersion     : 1
   +0x004 CurrentThread    : 0x814be200 _KTHREAD
   +0x008 NextThread       : (null)
   +0x00c IdleThread       : 0x814be200 _KTHREAD
   +0x010 LegacyNumber     : 0 ''
   +0x011 NestingLevel     : 0x1 ''
   +0x012 BuildType        : 0
   +0x014 CpuType          : 6 ''
   +0x015 CpuID            : 1 ''
   +0x016 CpuStep          : 0x3a09
   +0x016 CpuStepping      : 0x9 ''
   +0x017 CpuModel         : 0x3a ':'
   +0x018 ProcessorState   : _KPROCESSOR_STATE
   +0x338 ParentNode       : 0x8147d080 _KNODE
   +0x33c PriorityState    : 0x814a4948  ""
   +0x340 KernelReserved   : [14] 0
   +0x378 HalReserved      : [16] 0xa6a600
   +0x3b8 CFlushSize       : 0x40
   +0x3bc CoresPerPhysicalProcessor : 0x1 ''
   +0x3bd LogicalProcessorsPerCore : 0x1 ''
   +0x3be CpuVendor        : 0x1 ''
   +0x3bf PrcbPad0         : [1]  ""
   +0x3c0 MHz              : 0xd40
   +0x3c4 GroupIndex       : 0 ''
   +0x3c5 Group            : 0 ''
   +0x3c6 PrcbPad05        : [2]  ""
   +0x3c8 GroupSetMember   : 1
   +0x3cc Number           : 0
   +0x3d0 ClockOwner       : 0x1 ''
   +0x3d1 PendingTickFlags : 0x1 ''
   +0x3d1 PendingTick      : 0y1
   +0x3d1 PendingBackupTick : 0y0
   +0x3d2 PrcbPad10        : [70]  ""
   +0x418 LockQueue        : [17] _KSPIN_LOCK_QUEUE
   +0x4a0 InterruptCount   : 0xa968
   +0x4a4 KernelTime       : 0x5314
   +0x4a8 UserTime         : 0x208
   +0x4ac DpcTime          : 0x37
   +0x4b0 DpcTimeCount     : 0
   +0x4b4 InterruptTime    : 0x12
   +0x4b8 AdjustDpcThreshold : 2
   +0x4bc PageColor        : 0x320d
   +0x4c0 DebuggerSavedIRQL : 0x1c ''
   +0x4c1 NodeColor        : 0 ''
   +0x4c2 PrcbPad20        : [6]  ""
   +0x4c8 NodeShiftedColor : 0
   +0x4cc SecondaryColorMask : 0x3f
   +0x4d0 DpcTimeLimit     : 0
   +0x4d4 DeviceInterruptCount : 0x524c
   +0x4d8 PrcbPad21        : [2] 0
   +0x4e0 CcFastReadNoWait : 0
   +0x4e4 CcFastReadWait   : 0x1464
   +0x4e8 CcFastReadNotPossible : 0
   +0x4ec CcCopyReadNoWait : 0
   +0x4f0 CcCopyReadWait   : 0x1913
   +0x4f4 CcCopyReadNoWaitMiss : 0
   +0x4f8 MmSpinLockOrdering : 0n0
   +0x4fc IoReadOperationCount : 0n7105
   +0x500 IoWriteOperationCount : 0n5147
   +0x504 IoOtherOperationCount : 0n104510
   +0x508 IoReadTransferCount : _LARGE_INTEGER 0xbd1ff8a
   +0x510 IoWriteTransferCount : _LARGE_INTEGER 0x19bb1be
   +0x518 IoOtherTransferCount : _LARGE_INTEGER 0x55e32e
   +0x520 CcFastMdlReadNoWait : 0
   +0x524 CcFastMdlReadWait : 0
   +0x528 CcFastMdlReadNotPossible : 0
   +0x52c CcMapDataNoWait  : 0
   +0x530 CcMapDataWait    : 0xed39
   +0x534 CcPinMappedDataCount : 0xfa2
   +0x538 CcPinReadNoWait  : 0
   +0x53c CcPinReadWait    : 0x647
   +0x540 CcMdlReadNoWait  : 0
   +0x544 CcMdlReadWait    : 6
   +0x548 CcLazyWriteHotSpots : 0x2f
   +0x54c CcLazyWriteIos   : 0x211
   +0x550 CcLazyWritePages : 0x61c
   +0x554 CcDataFlushes    : 0x4b3
   +0x558 CcDataPages      : 0x16bd
   +0x55c CcLostDelayedWrites : 0
   +0x560 CcFastReadResourceMiss : 0
   +0x564 CcCopyReadWaitMiss : 0x944d
   +0x568 CcFastMdlReadResourceMiss : 0
   +0x56c CcMapDataNoWaitMiss : 0
   +0x570 CcMapDataWaitMiss : 0xe61
   +0x574 CcPinReadNoWaitMiss : 0
   +0x578 CcPinReadWaitMiss : 0x3a
   +0x57c CcMdlReadNoWaitMiss : 0
   +0x580 CcMdlReadWaitMiss : 0
   +0x584 CcReadAheadIos   : 0x851
   +0x588 KeAlignmentFixupCount : 0
   +0x58c KeExceptionDispatchCount : 0x2dc
   +0x590 KeSystemCalls    : 0x1e6ee1
   +0x594 AvailableTime    : 0x84
   +0x598 PrcbPad22        : [2] 0
   +0x5a0 PPLookasideList  : [16] _PP_LOOKASIDE_LIST
   +0x620 PPNxPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
   +0xf20 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
   +0x1820 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
   +0x2120 PacketBarrier    : 0
   +0x2124 ReverseStall     : 0n2
   +0x2128 IpiFrame         : (null)
   +0x212c PrcbPad3         : [52]  ""
   +0x2160 CurrentPacket    : [3] (null)
   +0x216c TargetSet        : 0
   +0x2170 WorkerRoutine    : (null)
   +0x2174 IpiFrozen        : 0
   +0x2178 PrcbPad4         : [40]  ""
   +0x21a0 RequestSummary   : 0
   +0x21a4 SignalDone       : (null)
   +0x21a8 PrcbPad50        : [40]  ""
   +0x21d0 InterruptLastCount : 0xa968
   +0x21d4 InterruptRate    : 4
   +0x21d8 DeviceInterrupts : 0x30
   +0x21dc IsrDpcStats      : 0x00000001 Void
   +0x21e0 DpcData          : [2] _KDPC_DATA
   +0x2210 DpcStack         : 0x827ec000 Void
   +0x2214 MaximumDpcQueueDepth : 0n4
   +0x2218 DpcRequestRate   : 4
   +0x221c MinimumDpcRate   : 3
   +0x2220 DpcLastCount     : 0x9dfd
   +0x2224 PrcbLock         : 0
   +0x2228 DpcGate          : _KGATE
   +0x2238 IdleState        : 0 ''
   +0x2239 QuantumEnd       : 0 ''
   +0x223a DpcRoutineActive : 0 ''
   +0x223b IdleSchedule     : 0 ''
   +0x223c DpcRequestSummary : 0n8
   +0x223c DpcRequestSlot   : [2] 0n8
   +0x223c NormalDpcState   : 0n8
   +0x223e ThreadDpcState   : 0n0
   +0x223c DpcNormalProcessingActive : 0y0
   +0x223c DpcNormalProcessingRequested : 0y0
   +0x223c DpcNormalThreadSignal : 0y0
   +0x223c DpcNormalTimerExpiration : 0y1
   +0x223c DpcNormalDpcPresent : 0y0
   +0x223c DpcNormalLocalInterrupt : 0y0
   +0x223c DpcNormalSpare   : 0y0000000000 (0)
   +0x223c DpcThreadActive  : 0y0
   +0x223c DpcThreadRequested : 0y0
   +0x223c DpcThreadSpare   : 0y00000000000000 (0)
   +0x2240 LastTimerHand    : 0x32ba
   +0x2244 LastTick         : 0x551c
   +0x2248 PeriodicCount    : 0
   +0x224c PeriodicBias     : 0
   +0x2250 ClockInterrupts  : 0x57bc
   +0x2254 ReadyScanTick    : 0x5565
   +0x2258 GroupSchedulingOverQuota : 0 ''
   +0x2259 ThreadDpcEnable  : 0x1 ''
   +0x225a PrcbPad41        : [2]  ""
   +0x2260 TimerTable       : _KTIMER_TABLE
   +0x3aa0 CallDpc          : _KDPC
   +0x3ac0 ClockKeepAlive   : 0n1
   +0x3ac4 PrcbPad6         : [4]  ""
   +0x3ac8 DpcWatchdogPeriod : 0n0
   +0x3acc DpcWatchdogCount : 0n1
   +0x3ad0 KeSpinLockOrdering : 0n0
   +0x3ad4 PrcbPad70        : [1] 0
   +0x3ad8 QueueIndex       : 1
   +0x3adc DeferredReadyListHead : _SINGLE_LIST_ENTRY
   +0x3ae0 ReadySummary     : 0
   +0x3ae4 AffinitizedSelectionMask : 0n262135
   +0x3ae8 WaitLock         : 0
   +0x3aec WaitListHead     : _LIST_ENTRY [ 0x9c6980dc - 0x97ac749c ]
   +0x3af4 ScbOffset        : 0x40
   +0x3af8 StartCycles      : 0x000002b9`cd5fac50
   +0x3b00 TaggedCyclesStart : 0
   +0x3b08 TaggedCycles     : [2] 0
   +0x3b18 GenerationTarget : 0x5538
   +0x3b20 CycleTime        : 0x00000009`eabff3e4
   +0x3b28 AffinitizedCycles : 0
   +0x3b30 HighCycleTime    : 9
   +0x3b34 PrcbPad71        : [11] 0
   +0x3b60 DispatcherReadyListHead : [32] _LIST_ENTRY [ 0x814a3c80 - 0x814a3c80 ]
   +0x3c60 ChainedInterruptList : (null)
   +0x3c64 LookasideIrpFloat : 0n2147483647
   +0x3c68 ScbQueue         : _RTL_RB_TREE
   +0x3c70 ScbList          : _LIST_ENTRY [ 0x89ac7100 - 0x89ac7100 ]
   +0x3c78 MmPageFaultCount : 0n612791
   +0x3c7c MmCopyOnWriteCount : 0n11038
   +0x3c80 MmTransitionCount : 0n184606
   +0x3c84 MmCacheTransitionCount : 0n0
   +0x3c88 MmDemandZeroCount : 0n368214
   +0x3c8c MmPageReadCount  : 0n102675
   +0x3c90 MmPageReadIoCount : 0n13991
   +0x3c94 MmCacheReadCount : 0n0
   +0x3c98 MmCacheIoCount   : 0n0
   +0x3c9c MmDirtyPagesWriteCount : 0n61586
   +0x3ca0 MmDirtyWriteIoCount : 0n421
   +0x3ca4 MmMappedPagesWriteCount : 0n0
   +0x3ca8 MmMappedWriteIoCount : 0n0
   +0x3cac CachedCommit     : 0x100
   +0x3cb0 CachedResidentAvailable : 0x79
   +0x3cb4 HyperPte         : 0x8001000f Void
   +0x3cb8 PrcbPad8         : [4]  ""
   +0x3cbc VendorString     : [13]  "GenuineIntel"
   +0x3cc9 InitialApicId    : 0 ''
   +0x3cca LogicalProcessorsPerPhysicalProcessor : 0x1 ''
   +0x3ccb PrcbPad9         : [1]  ""
   +0x3cd0 FeatureBits      : 0xa3cf3fff
   +0x3cd8 UpdateSignature  : _LARGE_INTEGER 0x00000017`00000000
   +0x3ce0 IsrTime          : 0
   +0x3ce8 PrcbPad90        : [2] 0
   +0x3cf0 PowerState       : _PROCESSOR_POWER_STATE
   +0x3e70 PrcbPad91        : [18] 0
   +0x3eb8 DpcWatchdogDpc   : _KDPC
   +0x3ed8 DpcWatchdogTimer : _KTIMER
   +0x3f00 HypercallPageList : _SLIST_HEADER
   +0x3f08 HypercallPageVirtual : 0xffd06000 Void
   +0x3f0c VirtualApicAssist : (null)
   +0x3f10 StatisticsPage   : (null)
   +0x3f14 Cache            : [5] _CACHE_DESCRIPTOR
   +0x3f50 CacheCount       : 4
   +0x3f54 PackageProcessorSet : _KAFFINITY_EX
   +0x3f60 SharedReadyQueueMask : 0
   +0x3f64 SharedReadyQueue : 0x814a4840 _KSHARED_READY_QUEUE
   +0x3f68 CoreProcessorSet : 1
   +0x3f6c ScanSiblingMask  : 0
   +0x3f70 LLCMask          : 1
   +0x3f74 CacheProcessorMask : [5] 1
   +0x3f88 ScanSiblingIndex : 0
   +0x3f8c WheaInfo         : 0x8035a170 Void
   +0x3f90 EtwSupport       : 0x803258c0 Void
   +0x3f98 InterruptObjectPool : _SLIST_HEADER
   +0x3fa0 PrcbPad92        : [3] 0
   +0x3fac PteBitCache      : 0x3fffff
   +0x3fb0 PteBitOffset     : 0x19b00
   +0x3fb4 PrcbPad93        : 0
   +0x3fb8 ProcessorProfileControlArea : (null)
   +0x3fbc ProfileEventIndexAddress : 0x814a40dc Void
   +0x3fc0 TimerExpirationDpc : _KDPC
   +0x3fe0 SynchCounters    : _SYNCH_COUNTERS
   +0x4098 FsCounters       : _FILESYSTEM_DISK_COUNTERS
   +0x40a8 Context          : 0x81a44340 _CONTEXT
   +0x40ac ContextFlagsInit : 0x1006f
   +0x40b0 ExtendedState    : 0x81a44000 _XSAVE_AREA
   +0x40b4 EntropyTimingState : _KENTROPY_TIMING_STATE
   +0x41dc IsrStack         : 0x827f0000 Void
   +0x41e0 VectorToInterruptObject : [208] (null)
   +0x4520 AbSelfIoBoostsList : _SINGLE_LIST_ENTRY
   +0x4524 AbPropagateBoostsList : _SINGLE_LIST_ENTRY
   +0x4528 AbDpc            : _KDPC
   +0x4548 IoIrpStackProfilerCurrent : _IOP_IRP_STACK_PROFILER
   +0x459c IoIrpStackProfilerPrevious : _IOP_IRP_STACK_PROFILER
   +0x45f0 TimerExpirationTrace : [16] _KTIMER_EXPIRATION_TRACE
   +0x46f0 TimerExpirationTraceCount : 5
   +0x46f4 ExSaPageArray    : 0x80250718 Void
   +0x46f8 PrcbPad100       : [10] 0
   +0x4720 LocalSharedReadyQueue : _KSHARED_READY_QUEUE

// 멤버 추가 됨.. 천천히 수정

 

 

이번 포스트의 시작과 함께…

이런 경우는 도대체 언제 발생하는가 생각이 든다…

왜 8바이트를 컴파일러에서는 이런 식으로 처리하게 되었는가? 부터 시작해서

VC 컴파일러는 믿을만 한가? 라는 생각도 들게하는 문제이다.

일단 문제는 잘 사용되던 코드에서 갑자기 에러가 나면서 시작됐다.

에러가 난 코드는 다음 그림과 같다.

VC를 조금이라도 다뤄본 사람은 0xCC로 채워진 메모리는 초기화되지 않은 영역을 의미한다는 것을 알것이다. 근데 나는 분명히 0으로 초기화를 했으며… 8바이트 중 4바이트만 일부러 0xcc를 채우지도 않았다.

아래는 위 상태의 this 메모리 영역이다. (m_nDTB를 채우기전)

 

첫 4바이트는 vftable을 나타낸다. 문제는 그 뒤 메모리를 어떻게 사용하느냐인데… 아래를 디스어셈블 내용을 보면 알 수 있다.

 

디스어셈블을 보면 전달 인자인 _nDTB를 스택에 하위 4바이트를 넣고 edx에 상위 4바이트를 전달하여 처리한다. 처리할 때 메모리 영역을 보면 this + 0x08 부터 4바이트, 4바이트 단위로 8바이트 변수인 m_nDTB 변수에 채워넣는 것을 볼 수 있다. edx는 당연히 0이다.

이쯤 되면 코드에는 아무 문제가 없어보인다. 그런데 다시 맨 위 m_nDTB 값을 보면 이상하다는 것을 느낄 수 있을 것이다. 위 코드로 볼 때 m_nDTB가 메모리 상에서는 0으로 채워져있다는 것을… 하지만 실제 m_nDTB 값은 아래와 같이 값을 채운 후에도 이상하다는 것을…

 

위 그림은 Address 부분에  &m_nDTB를 입력했을 때 나온 결과이다. 즉, 실제 8 바이트 m_nDTB 변수 사용은 this + 0x04 인 영역부터 8바이트인 것이다. 왜 이런 상황이 발생했을까?

  1. 단순히 디버깅 정보 표시 잘못이다. m_nDTB는 0x0000000000185000으로 사용된다.
  2. 컴파일 시 무언가 잘못되었다.

1번인지 알고 release 모드로 실행했지만 m_nDTB가 쓰레기 값으로 사용되는 것을 확인하였다. 그럼 2번인데… 컴파일러가 왜 이걸 햇갈렸을까 아무리 생각해봤자 답이 안나오니…

컴파일러야 햇갈리지마 T^T 라는 생각으로 메모리 정렬 단위를 명시해주었다.

#pragma pack(4)

적어주니 아래와 같이 값이 컴파일러는 잘 판단해주었다. ㅡㅡ...

 

32비트 컴파일 시 기본 컴파일 단위가 4바이트라고 알고 있는데… 무엇이 잘못되었을까….

결론 : VC 2010 버전에서는 메모리 정렬 단위 선언으로 컴파일러에 확실히 명시해주자

   

'디버깅' 카테고리의 다른 글

MS 심볼 구성에 관하여  (0) 2014.12.17
Windows 10 _KPCR and _KPRCB  (0) 2014.11.14
Windbg에서 KDBG 찾기  (0) 2014.06.19
Window8 Remote Kernel Debugging on Vmware using WinDbg  (0) 2014.04.11
_DRIVER_OBJECT.DriverSection 정보  (0) 2013.10.05

wofstream을 사용하여 로그를 기록하는데 이상하게 한글만 출력이 안되었다. 입력 버퍼에도 잘 기록되었는데 안되다니 뭔가 이상했다. 출력된 로그 파일을 보니 영문과 숫자까지만 출력이 되고 그 뒤로 출력되어야할 한글 문자열부터 출력이 되지 않았다. 궁금해서 헥스 편집기로 열어서 보니 아래와 같이 아스키코드로 저장되어 있는 것을 확인할 수 있었다.

   

   

이 무슨 어이없는 일이란 말인가? wofstream으로 출력했으니 당연히 wide 문자로 나와야한다고 생각했는데 큰 오산이었다. 뭐 결국에 해결하고 생각해보면 당연한게 wofstream은 wchar_t 형태의 문자열을 파일로 출력하는 클래스기 때문에 파일 최적화를 위해서 굳이 wchar_t 형태로 내보낼 필요가 없다는 것이다.

wofstream에서 한글 출력을 설정하기 위해서는 코드에 간단하게

   

std::locale::global(std::locale("Korean"));

   

를 추가하면 된다. 하지만 코드의 어디다 넣어야 될까? 파일 쓰기 함수 전에? 파일 오픈 전에? 답은 wofstream의 인스턴스가 생성되기 전이다. (이거 떄문에 시간을 얼마나 소비했는지…) 아래는 이를 증명하는 코드이다.

   

   

코드에 적혀있는데로 wofstream은 인스턴스가 생성될 때 locale이 설정된다. 이 설정된 locale은 이후에 locale이 변경되어도 생성된 wofstream에 그대로 유지된다. 아마도 스트림별 다양한 locale을 지원하기 위해 이렇게 했나 싶다. 뭐.. 이것도 생각해보면 당연하지만…

코드는 locale 설정 전에 메모리에 할당된 스트림과 설정 후에 메모리에 할당된 스트림을 사용하여 파일에 한글 출력을 테스트한다. 위 코드를 컴파일 후 실행해보면 아래와 같이 locale 설정 전에 할당된 wofstream은 0kb로 한글을 출력하지 못한다.

   

   

이렇게 해결 방법을 알아도 문제가 되는 경우가 있는데 전역 변수(정적 변수)의 경우 locale 설정보다 먼저 메모리에 할당될 수 있다. 어쨌든 문제는 해결했으니 … bye!

   

이번 포스트 요약

wofstream은 기본 설정 사용시 wchar_t 형태의 파일 출력을 보장하지 않는다.

wofstream 인스턴스 생성 전, 즉 메모리에 할당되기 전에 locale 설정을 해야한다.

   

코드

int _tmain(int argc, _TCHAR* argv[])

{

/*

wofstream은 메모리에 할당될 때 locale이 설정된다.

이 설정된 locale은 이후에 locale이 변경되어도 유지된다.

*/

std::wofstream fstream_locale_before;

std::locale::global(std::locale("Korean"));

std::wofstream fstream_locale_after;

   

fstream_locale_before.open( _T("..\\test before locale setting.txt") );

fstream_locale_before << _T("한글 파일 출력 테스트") << std::endl;

fstream_locale_before.close();

   

fstream_locale_after.open( _T("..\\test after locale setting.txt") );

fstream_locale_after << _T("한글 파일 출력 테스트") << std::endl;

fstream_locale_after.close();

   

return 0;

}

   

'프로그래밍' 카테고리의 다른 글

Rstudio 3.3.0 스케일링 문제  (0) 2016.05.06
드라이버 개발 삽질 #1  (0) 2015.03.12
ifstream 64  (0) 2014.09.25
CPP 벡터 반복문 중 원소 지우기  (0) 2014.09.22
가상 함수 테스트  (0) 2014.07.05

ifstream 64

프로그래밍2014. 9. 25. 02:40

2년 전 쯤에 대용량 파일(2GB 이상의 파일) 처리가 가능한 프로그램을 만들어야 했다. 32비트 시스템에서는 4바이트 크기의 파일 포인터를 사용하기에 이런 파일에 대해 입출력 작업을 할 때 윈도우 API를 제외한 일반적인 방법으로 처리하기가 힘들었다. 지금이야 윈도우 API의 강력함을 알고 있지만서도 그 때 당시 크로스컴파일에 심취해있었기 때문에 윈도우 API로 파일 처리하는 것은 내 자존심이 허락하지 않았다.

직접 알아본 파일 처리 방법은 64비트 환경에서 컴파일 그리고 _fseeki64 류의 함수를 사용, fstream을 8바이트 파일 포인터를 지원하게 일반화 프로그래밍을 하는 것이었다. 급한데로 _fseeki64 류의 함수를 사용하여 잘 처리하였지만 코딩에 미련이 안남을 수 있겠는가? 시간이 남을 때 64비트 파일 포인터를 지원하는 fstream을 만드는 방법을 찾아보았다. 아래는 fstream64를 지원하는 코드이다.

   

#ifdef WIN32

typedef int64_t streamoff64;

class streampos64
{
        typedef streampos64 _Myt;
        typedef _Mbstatet _Statetype;
public:
        streampos64(streamoff64 _Off = 0)
                : _Myoff(_Off), _Fpos(0), _Mystate(0)
                {        // construct with stream offset
                }

streampos64(_Statetype _State, fpos_t _Fileposition)
                : _Myoff(0), _Fpos(_Fileposition), _Mystate(_State)
                {        // construct with conversion state and C file position
                }

_Statetype state() const
                {        // return conversion state
                return (_Mystate);
                }

   

직접 코드를 가져다 사용하면 C4927 에러가 나는데… 이게 여간 신경 쓰이는게 아니다.

   

c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(424): warning C4927: illegal conversion; more than one user-defined conversion has been implicitly applied

1> while calling the constructor 'streampos64::streampos64(streamoff64)'

1> c:\~\streamutil.h(15) : see declaration of 'streampos64::streampos64'

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(423) : while compiling class template member function 'streampos64 std::basic_streambuf<_Elem,_Traits>::seekoff(unsigned __int64,std::ios_base::seekdir,std::ios_base::openmode)'

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\fstream(135) : see reference to class template instantiation 'std::basic_streambuf<_Elem,_Traits>' being compiled

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\fstream(889) : see reference to class template instantiation 'std::basic_filebuf<_Elem,_Traits>' being compiled

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\~~~~~\header.h(63) : see reference to class template instantiation 'std::basic_ifstream<_Elem,_Traits>' being compiled

(생략)

   

C4927 에러는 warning 1단계 에러로 무진장 잘나온다. 한국어 버전은 어떻게 나오는지 모르겠지만 해석해보면 네가 정의한 데이터에 대해 암묵적으로 데이터 변환이 일어났는데 이를 해결 안하면 어떤 문제가 생길 수도 있다는 것이다. 이걸 한번 해결해보는게 바로 이번 포스트의 내용이다.

CPP에서 템플릿을 사용하는 함수나 클래스는 컴파일 시간에 그 타입이 결정된다. VC는 이에 대한 로그를 스택 형식으로 보여주기 때문에 관련 로그를 살펴보기 위해서는 맨 아래부터 살펴보면 아래 15번째 줄의 코드 라인부터 컴파일이 일어났고 이에 대한 워닝이 떴다는 것을 알 수 있다.

   

   

다음은 basic_ifstream에서 발생하는데 typedef basic_filebuf<_Elem, _Traits> _Myfb에서 워닝이 발생한다. 내용은 _Filebuffer를 만드는데 여기서 워닝이 발생한다~ 그리고 이 변수는 basic_filebuf에 속해있다 이다.

   

   

   

이제 실제 워닝 발생 지점이다.

   

   

ifstream64은 다음과 같이 아래의 소스 코드에 나와있듯이 basic_ifstream에 출력 문자열의 형태와 관리 방법을 정의하는 객체인 _Traits를 새롭게 정의한다. 여기서 주목해야될 것은 바로 pos_type과 off_type이다.

   

template<typename Elem>

class char_traits64 : public std::char_traits<Elem>

{

public:

typedef Elem char_type;

typedef long int_type;

typedef streampos64 pos_type;

typedef streamoff64 off_type;

typedef _Mbstatet state_type;

};

   

typedef std::basic_ifstream<char, char_traits64<char> > ifstream64;

   

일반적으로 ifstream에서 사용되는 char_traits는 아래와 같이 streampos와 streamoff 를 사용한다. 여기서 사용되는 streampos는 fpos<_Mbstatet>이며 streamoff는 32비트 정수형이다. ( * 64비트 환경에서는 64비트 정수형이다. )

   

   

소스코드에서 streamoff64는 uint64_t로 64비트 정수형이다. streampos64는 fpos<_Mbstatet>를 64비트에 맞춰서 새롭게 정의를 했다. 워닝은 바로 이 부분에서 발생하는 것이다. streampos64를 다시 살펴보면 생성자 부분에서 워닝이 발생한다는 것을 알 수 있다. 그렇다면 생성자에서 왜 워닝이 발생하는건가?

   

   

seekoff 함수의 return 문을 보면 streampos(~)를 반환한다. 하지만 return 데이터 형식은 pos_type으로 컴파일시에는 streampos64로 결정된다. 바로 여기에서 컴파일러는 데이터 형식이 정수형으로 비슷하지만 클래스에서 어떻게 처리할지 모르기 때문에 암묵적으로 데이터 변환이 일어나고 있다고 말해주는 것이다.

   

   

이를 해결하는 방법은 streampos에 해당하는 새로운 생성자를 만들어주면 워닝이 금방 해결 된다.

   

public:

streampos64(streamoff64 _Off = 0)

: _Myoff(_Off), _Fpos(0), _Mystate(0)

{        // construct with stream offset

}

   

streampos64(std::streampos _Off)

: _Myoff(_Off), _Fpos(0), _Mystate(0)

{        // construct with stream offset

}

   

fstream64를 사용하면서 선언한 곳마다 warning이 20~30줄 떠버리면 무진장 신경쓰인다. 거기다 왠지 모르게 C4927 워닝은 무시도 안된다. 이상 마친다~

   

그런데 64비트 환경에서 32비트로 컴파일하면 파일 포인터는 32비트일까 64비트일까? -_-??? 흠..

   

ps. boost::filesystem::fstream 도 있네? 봐봐야겠다.

일반적으로 벡터의 원소를 지울 때 erase를 사용하는데 이게 for 문을 돌리면서(iterator++를 하면서) 조건에 맞을 때 삭제하는 코드를 짜면 erase 후 iterator 변수가 가볍게 크래쉬를 발생시켜주신다. 할당해제하고 나면 어떻게 되는지 VS 환경에서 디버거로 본적이 있는데 iterator 변수가 할당해제한 원소를 계속 가리키고 있었던 것으로 기억난다. 아무튼 깔끔하게 프로그래밍 할려면 아래처럼...


std::vector<int *> vIntegers;

// fill integers in the vector.

for(int i = 0; i < 100; i++) {

vIntegers.push_back(new int(i));

}


auto it = begin(vIntegers);

while(it != end(vIntegers)) {

if(**it % 2 == 0) {

printf("%d ", **it);

it++;

} else {

// delete allocated memory of 4bytes, integer size

delete *it;

// erase returns next iterator of removed iterator 

it = vIntegers.erase(it);

}

}


아니면 삭제하는 부분을 그대로 for 문으로 나둬도 괜찮다.

for(auto it = begin(vIntegers); it != end(vIntegers);) {

while(it != end(vIntegers)) {

if(**it % 2 == 0) {

printf("%d ", **it);

it++;

} else {

// delete allocated memory of 4bytes, integer size

delete *it;

// erase returns next iterator of removed iterator 

it = vIntegers.erase(it);

}

}

}


for 문이 내게는 한 줄에 많은 정보를 줘서 더 가독성이 있다.

'프로그래밍' 카테고리의 다른 글

드라이버 개발 삽질 #1  (0) 2015.03.12
ofstream 한글 출력 문제  (0) 2014.10.22
ifstream 64  (0) 2014.09.25
가상 함수 테스트  (0) 2014.07.05
최상위 윈도우 핸들로부터 모든 자식 윈도우 핸들 얻기  (0) 2014.03.27

Quiet - 내향적인 사람들을 위한 또는 이해하기 위한 도서

  모두 알고 지내던 시골 동네에서 ‘인격’을 중시하던 문화는 도시로 오면서 ‘성격’을 중시하는 문화로 바뀌었다. 이렇게 산업화 시대를 지나며 외향성은 우리 문화의 이상으로 자리 잡았다. 하지만 과연 외향성이 답일까? 내향적인 사람들은 반드시 성격을 고쳐야 하는가? 라는 주제로 후기를 써내려가는 것도 좋겠지만 이 답은 책에 있기에 나의 경험에 비추어 ‘내향적인 사람이 살아야 하는 방식’에 대해 쓴다.

  6년 전 나는 군대에서 살아남기 위해 외향의 가면을 썼다. 모두 처음 만나는 사람이었고 내향적이었던 나로서는 그들을 흉내 낼 수밖에 없었다. 더욱이 그들 사이에서는 소위 성격 좋다고 말하는 사람이 다른 사람의 인정도 받고 잘나갔기 때문에 나 또한 그들을 닮기 위해 더욱 노력했다. 하지만 군대에서 하루하루가 너무 피곤했다. 이 책을 읽기 전까지 나는 그 피곤이 정신적 피곤이 아닌 단순한 육체적 피곤이라 생각했다.

  외향적인 사람들과는 다르게 내향적인 사람들은 외부 자극에 대한 반응이 낮은 편이다. 외향적인 사람은 새로운 사람을 만나고, 스키를 타고, 높은 볼륨의 음악 등의 강렬한 자극을 즐기지만 내향적인 사람들은 가까운 친구와 차를 마시거나 책을 읽는 정도가 ‘딱 맞다’고 느낀다. 이런 반응성은 각 성격이 소모한 에너지를 충전하는 방법으로 이어진다. 내향적인 사람은 자신만의 공간에서 에너지를 회복하고 그 곳에서 벗어나는 순간부터 에너지를 소모한다. 반면에 외향적인 사람들은 사람들이 많은 공간에서 에너지를 얻는다.

순전히 외향적인 사람이나 순전히 내향적인 사람 같은 건 없다. 그런 사람은 정신병동에 들어가 있을 것이다. by 칼 융

  융이 말했듯이 성격은 복잡한 것이어서 어느 순간에서는 내향적인 사람도 외향적으로 변할 수 있다. 대부분의 내향적인 사람들은 핵심 목표가 생기면 자신의 기질을 뛰어넘어 외향적인 사람처럼 행동할 수 있다. 하지만 이런 가면을 쓰는 행위는 일정 시간만 지속하되 나머지 시간은 반드시 자신의 모습 그대로 지낼 수 있어야 한다. 감정을 다스리고 바꾸려고 할 때 들어가는 ‘감정 노동’은 절대 가벼운 게 아니기 때문이다.

  가면을 쓰고 회복 환경 없이 지속되면 내향적인 사람은 어떻게 될까? 처음엔 정신적으로 탈진하게 되고 점점 신체적으로 탈진하게 된다. 이게 바로 내가 군대에서 뿐만 아니라 사회에 나와서도 피곤했던 이유이다. 내향적인 사람들은 양심적인 자세 때문에 자신이 감당할 수 있는 범위를 넘어선 만큼을 떠맡게 될 때, 평소라면 재미있을 일에도 흥미를 잃게 되며 건강도 나빠질지 모른다. 따라서 내향적인 사람들은 반드시 소모한 에너지를 자신만의 회복 공간에서 충전해야 한다.

  흔히들 내향적인 사람은 사교성이 부족하다고 걱정한다. 하지만 내향적이라도 사람하고 어울리는 걸 못하는 게 아니다. 사무적인 대화나 공적인 대화는 충분히 가능하다. 친구랑 노는 게 마냥 불편한 것도 아니다. 다만 혼자가 편하고 자기만의 공간이 필요한 것 뿐이다. 사교성이 부족한 문제는 외향적인 사람들에게서도 나타나는 부분이다.

  이런 내용 외에도 가면의 여부를 떠나 자신이 외향적인지 내향적인지 판단하는 방법, 내향적인 아이를 기르는 방법, 외향적인 남편과 내향적인 부인이 잘 지내는 방식 등 내향적인 사람들에게 좋은 내용이 나오니 읽어보면 참 좋을 것 같다.


ps. 내향성에 대한 만화 (http://sg-mh.com/viewer/2120999)

'생활' 카테고리의 다른 글

논문 영어 쓰기  (0) 2015.04.20
'배려'의 명언  (0) 2014.11.24
메모리 포렌식 도서 출간 예정  (0) 2014.04.11
다음팟 로컬 채팅무시 프로그램  (1) 2014.02.15
나니아 연대기  (0) 2013.02.12

class TestParent {
public:
	virtual void foo(void) {
		printf("call TestParent::foo()\n");
	}
	virtual void bar(void) {
		printf("call TestParent::bar()\n");
	}

public:
	int m_nMember1;
};

class TestChild1 
	: public TestParent {
public:
	void foo(void) {
		printf("call TestChild1::foo()\n");
	}

	int m_nMember2;
};

class TestChild2 
	: public TestParent {
public:
	void foo(void) {
		printf("call TestChild2::foo()\n");
	}

	int m_nMember2;
};

class TestChild3 
	: public TestParent {
public:
	void foo(void) {
		printf("call TestChild3::foo()\n");
	}

	int m_nMember2;
};

int main() {
	std::vector vTestCls;

	vTestCls.push_back(new TestParent);
	vTestCls.push_back(new TestChild1);
	vTestCls.push_back(new TestChild2);
	vTestCls.push_back(new TestChild3);

	for(auto p = begin(vTestCls); p != end(vTestCls); p++) {
		TestParent* pTestParent = *p;
		pTestParent->foo();
		pTestParent->bar();
	}
	
	void (TestParent::*pfn_parent_foo)(void);
	void (TestChild1::*pfn_child1_foo)(void);
	void (TestChild2::*pfn_child2_foo)(void);
	void (TestChild2::*pfn_child3_foo)(void);

	TestChild1* pTestChild1 = new TestChild1;
	pfn_child1_foo = &TestChild1::foo;

	for(auto p = begin(vTestCls); p != end(vTestCls); p++) {
		pfn_parent_foo = &TestParent::foo; 
		// 함수의 호출 주소만 가짐(인스턴스 X)
		TestParent* pTestParent = *p;

		pTestParent->m_nMember1 = 0x41414141;
		(pTestParent->*pfn_parent_foo)(); 
		// 인스턴스에서 함수의 주소를 이용하여 해당 함수를 호출
		// 결국 vtable을 또 거치네

		pTestParent->bar();
	}

	return 0;
}

  Windbg의 타겟 시스템은 wdbgexts.h에 정의되어 있는 _KDDEBUGGER_DATA64 구조체(이하 KDBG)를 이용하여 그 디버깅 정보를 유지한다. 또한 Windbg는 심볼 정보가 담긴 pdb 파일을 이용하여 구조체들의 필드 구성과 해당 값을 볼 수 있다. 이런 구조체 조사를 아래와 같이 "dt" 명령어를 통해 지원한다.


kd> dt _EPROCESS

nt!_EPROCESS

+0x000 Pcb : _KPROCESS

+0x2c8 ProcessLock : _EX_PUSH_LOCK

+0x2d0 CreateTime : _LARGE_INTEGER

+0x2d8 RundownProtect : _EX_RUNDOWN_REF

+0x2e0 UniqueProcessId : Ptr64 Void

+0x2e8 ActiveProcessLinks : _LIST_ENTRY

+0x2f8 Flags2 : Uint4B

+0x2f8 JobNotReallyActive : Pos 0, 1 Bit

(생략)


  하지만 타겟 시스템(Debuggee) 내에서도 유용하게 사용되는 KDBG 구조체 정보는 dt 명령어를 통해 한눈에 수가 없는데, 이유는 KDBG 구조체에 대한 심볼 정보가 없기 때문이다. 사실 KDBG 구조체를 누가 자주 찾아보겠는가 하지만… 가끔씩 KDBG 구조체를 봐야할 때가 있는데 때마다 아래와 같은 방법으로 KDBG 구조체를 찾았다.


Version 정보

Microsoft (R) Windows Debugger Version 6.3.9600.16384 AMD64

Copyright (c) Microsoft Corporation. All rights reserved.

   

Opened \\.\pipe\com_8_64

Waiting to reconnect...

Connected to Windows 8 9200 x64 target at (Tue Jun 17 19:40:42.866 2014 (UTC + 9:00)), ptr64 TRUE

Kernel Debugger connection established.

   

Windbg에서 KDBG 찾기

dq KdDebuggerDataBlock 

하면 된다. ^_^ 아 괜히 부끄러워지네... 

아래는 뭐.. 남겨둔다. 소스 코드도 수정해서 다시 올려야겠다.

  1. KdVersionBlock의 주소를 얻는다.

kd> dq KdVersionBlock

fffff803`5f2fddf0 00070206`23f0000f 00000031`030c8664

fffff803`5f2fde00 fffff803`5f08a000 fffff803`5f354a60

fffff803`5f2fde10 fffff803`5f30a8d0 ffffffff`ffffffff

fffff803`5f2fde20 00000000`00140014 00000000`00000000

fffff803`5f2fde30 00000000`00000000 00000000`00000000

fffff803`5f2fde40 00000000`00000000 00000000`00000000

fffff803`5f2fde50 00000000`00000000 00000000`00000000

fffff803`5f2fde60 00000000`00000000 00000000`00000000

   

  이 KdVersionBlock은 Windbg의 커널의 전역 변수로 디버깅 시스템에 대한 버전 정보를 지닌 _DBGKD_GET_VERSION64 구조체의 주소를 가지고 있다. 또한 이 구조체의 DebuggerDataList 필드는 KDBG 리스트의 주소를 가지고 있다. 리스트라고 했지만 사실 KDBG는 하나만 존재한다. 리스트는 확장성을 위해서 그런 형태로 남겨둔 것 같다.

   

kd> dt nt!_DBGKD_GET_VERSION64 fffff803`5f2fddf0

+0x000 MajorVersion : 0xf

+0x002 MinorVersion : 0x23f0

+0x004 ProtocolVersion : 0x6 ''

+0x005 KdSecondaryVersion : 0x2 ''

+0x006 Flags : 7

+0x008 MachineType : 0x8664

+0x00a MaxPacketType : 0xc ''

+0x00b MaxStateChange : 0x3 ''

+0x00c MaxManipulate : 0x31 '1'

+0x00d Simulation : 0 ''

+0x00e Unused : [1] 0

+0x010 KernBase : 0xfffff803`5f08a000

+0x018 PsLoadedModuleList : 0xfffff803`5f354a60

+0x020 DebuggerDataList : 0xfffff803`5f30a8d0 // -> _PKDDEBUGGER_DATA64

   

  1. DebuggerDataList를 통해 KDBG의 주소를 얻는다.

       

kd> dq 0xfffff803`5f30a8d0

fffff803`5f30a8d0 fffff803`5f2fda90 fffff803`5f2fda90

fffff803`5f30a8e0 00001666`754785db 00001666`754785db

fffff803`5f30a8f0 00000000`00000000 00000000`00da7a64

fffff803`5f30a900 526d6574`7379535c 74737973`5c746f6f

fffff803`5f30a910 6f746e5c`32336d65 78652e6c`6e726b73

fffff803`5f30a920 00000000`00000065 00000000`00000000

fffff803`5f30a930 00000000`00000000 00000000`00000000

fffff803`5f30a940 00000000`00000000 00000000`00000000


 KDBG의 주소는 fffff803`5f2fda90로 LIST_ENTRY64의 형태로 생각하면 FLINK 부분이라고 보면 된다. 물론 바로 옆 8바이트를 보면 BLINK 부분도 같은 값으로 채워져 있는 것을 확인할 수 있다.

   

kd> dc fffff803`5f2fda90

fffff803`5f2fda90 5f30a8d0 fffff803 5f30a8d0 fffff803 ..0_......0_....

fffff803`5f2fdaa0 4742444b 00000360 5f08a000 fffff803 KDBG`......_....

fffff803`5f2fdab0 5f0ff930 fffff803 00000000 00000000 0.._............

fffff803`5f2fdac0 00000000 00010000 5f0ff510 fffff803 ..........._....

fffff803`5f2fdad0 00000000 00000000 5f354a60 fffff803 ........`J5_....

fffff803`5f2fdae0 5f320c10 fffff803 5f3e0188 fffff803 ..2_......>_....

fffff803`5f2fdaf0 5f319ac0 fffff803 5f3e21c0 fffff803 ..1_.....!>_....

fffff803`5f2fdb00 5f3e00a8 fffff803 5f3e00bc fffff803 ..>_......>_....

   

 Tag(_KDDEBUGGER_DATA64.Header.Tag)값을 확인하여 KDBG 구조체를 확인할 수 있다. 이렇게 KDBG를 찾으면 또 문제가 생기는데 구조체 심볼 정보가 없기 때문에 원하는 필드의 값을 일일이 오프셋을 계산하여 알아내야 한다. 너무 귀찮다… 그래서 공부도 할 겸 Windbg Extesion 형태로 kdbgext.dll 플러그인을 만들었다.

   

KDBG EXTENSION Plugin

  최대한 dt와 비슷하게 나오게 할려고 했지만 아직 부족한건 사실이다. 백문이 불여일견이라고 아래를 보면 어떻게 동작하는지 대충 알 수 있다.

   

kd> .load kdbgext.dll

kd> !kdbg

KDBG Windbg Extension v0.1 by ozdang

> PlatformId : 2 / Win32 : 6.2 / KD : 15.9200

> KdVersionBlock        : 0xfffff80355af9df0

> DebugDataList        : 0xfffff80355b068d0

> KDBG's Address        : 0xfffff80355af9a90

dt _KDDEBUGGER_DATA64 0xfffff80355af9a90

+00000 Header.List.Flink        : 0xfffff80355b068d0

+0x008 Header.List.Blink        : 0xfffff80355b068d0

+0x010 Header.OwnerTag        : 0x4742444b 'KDBG'

+0x014 Header.Size        : 0x360

+0x018 KernBase        : 0xfffff80355886000

+0x020 BreakpointWithStatus        : 0xfffff803558fb930

+0x028 SavedContext        : 0

+0x030 ThCallbackStack        : 0

+0x032 NextCallback        : 0

+0x034 FramePointer        : 0

+0x036 Header.Size        : 0x1

+0x038 KiCallUserMode        : 0xfffff803558fb510

+0x040 KeUserCallbackDispatcher        : 0

+0x048 PsLoadedModuleList        : 0xfffff80355b50a60

+0x050 PsActiveProcessHead        : 0xfffff80355b1cc10

(생략)

   

  Windbg x86 버전에서만 동작한다. 세팅 방법은 Windbg Extension 폴더에 넣으면 된다. WDK 8.1 버전의 경우 "C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\winext" 이다. 이후 위와 같이 .load kdbgext.dll로 플러그인을 불러오고 !kdbg 명령어를 통해 KDBG 구조체의 정보를 볼 수 있다.

  본 플러그인의 소스는 https://code.google.com/p/kdbg-extension/ 에서 볼 수 있으며 다운로드도 가능하다.

   

Download : 

kdbgext.dll


*여담이지만 처음만들 때는 pdb 파일을 만들어서 심볼 정보만 얻을려고 했는데 뭔가 잘 되지 않았다… 이 방법으로 될 것 같기도 하고 아닌 것 같기도 하고… 해서 일단 확실한 플러그인 형태로 제작하였다.

'디버깅' 카테고리의 다른 글

MS 심볼 구성에 관하여  (0) 2014.12.17
Windows 10 _KPCR and _KPRCB  (0) 2014.11.14
8바이트 변수 초기화 문제  (0) 2014.11.14
Window8 Remote Kernel Debugging on Vmware using WinDbg  (0) 2014.04.11
_DRIVER_OBJECT.DriverSection 정보  (0) 2013.10.05