Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 205. Windbg - KPCR, KPRCB [링크 복사], [링크+제목 복사],
조회: 4649
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 6개 있습니다.)
VC++: 64. x64 Visual C++에서 TEB 주소 구하는 방법
; https://www.sysnet.pe.kr/2/0/1387

.NET Framework: 348. .NET x64 응용 프로그램에서 Teb 주소를 구하는 방법
; https://www.sysnet.pe.kr/2/0/1388

.NET Framework: 349. .NET Thread 인스턴스로부터 COM Apartment 유형 확인하는 방법
; https://www.sysnet.pe.kr/2/0/1389

VC++: 91. 자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드
; https://www.sysnet.pe.kr/2/0/10797

디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
; https://www.sysnet.pe.kr/2/0/12097

디버깅 기술: 205. Windbg - KPCR, KPRCB
; https://www.sysnet.pe.kr/2/0/13842




Windbg - KPCR, KPRCB

프로세서가 사용자 모드의 코드를 실행하는 동안에는 GS 레지스터가 TEB 영역을 가리키지만, 커널 모드에서는 PCR 및 PRCB 값을 담은 영역을 가리킵니다.

// Fooling Windows about its internal CPU
// https://rayanfam.com/topics/fooling-windows-about-cpu/

// nt!_KPCR
// https://codemachine.com/articles/kernel_structures.html#KPCR

// (local kernel 디버깅이 아닌) live 커널 디버깅 모드 상태 (vCPU == 8개인 경우)

0: kd> dq gs:[0]
002b:00000000`00000000  fffff800`4e0c9fb0 fffff800`4e0c8000
002b:00000000`00000010  00000047`fdacf648 fffff800`4cf53000
002b:00000000`00000020  fffff800`4cf53180 fffff800`4cf53870
002b:00000000`00000030  00000047`fdc38000 fffff800`4e0c7000
002b:00000000`00000040  00000000`00000000 00000000`00000000
002b:00000000`00000050  00000000`00001000 00000000`00000000
002b:00000000`00000060  00000e10`00010001 00000000`00000000
002b:00000000`00000070  00000000`00000000 00000000`00000000

0: kd> ~7

7: kd> dq gs:[0]
002b:00000000`00000000  ffff8100`766b2fb0 ffff8100`766b1000
002b:00000000`00000010  00000047`fde7f228 ffff8100`766a2000
002b:00000000`00000020  ffff8100`766a2180 ffff8100`766a2870
002b:00000000`00000030  00000047`fdc40000 ffff8100`766b0000
002b:00000000`00000040  00000000`00000000 00000000`00000000
002b:00000000`00000050  00000000`00071000 00000000`00000000
002b:00000000`00000060  00000e10`00010001 00000000`00000000
002b:00000000`00000070  00000000`00000000 00000000`00000000

gs:[0]의 출력에서 어떤 오프셋에 KPCR과 KPRCB 값을 담고 있는지 확인하고 싶다면 각각 !pcr, !prcb 명령어로 알아낼 수 있는데요,

7: kd> !pcr
KPCR for Processor 7 at ffff8100766a2000:
    Major 1 Minor 1
    NtTib.ExceptionList: ffff8100766b2fb0
        NtTib.StackBase: ffff8100766b1000
       NtTib.StackLimit: 00000047fde7f228
     NtTib.SubSystemTib: ffff8100766a2000
          NtTib.Version: 00000000766a2180
      NtTib.UserPointer: ffff8100766a2870
          NtTib.SelfTib: 00000047fdc40000

                SelfPcr: 0000000000000000
                   Prcb: ffff8100766a2180
                   Irql: 0000000000000000
                    IRR: 0000000000000000
                    IDR: 0000000000000000
          InterruptMode: 0000000000000000
                    IDT: 0000000000000000
                    GDT: 0000000000000000
                    TSS: 0000000000000000

          CurrentThread: ffff9485cf602080
             NextThread: 0000000000000000
             IdleThread: ffff9485cf602080

              DpcQueue: 
7: kd> !prcb
PRCB for Processor 7 at ffff8100766a2180:
Current IRQL -- 0
Threads--  Current ffff9485cf602080 Next 0000000000000000 Idle ffff9485cf602080
Processor Index 7 Number (0, 7) GroupSetMember 80
Interrupt Count -- 00006b89
Times -- Dpc    00000001 Interrupt 00000000 
         Kernel 00003191 User      00000009 

그러니까, (물론 운영체제 버전에 따라서도 달라질 수 있지만) 대충 이렇게 정리할 수 있습니다.

// Windows 10 x64인 경우

gs:[18] == KPCR     == ffff8100`766a2000 == Processor Control Region
gs:[20] == KPRCB    == ffff8100`766a2180 == Processor Control Block

KPCR/KPRCB는 이름에서도 알 수 있듯이 물리 장치인 CPU를 소프트웨어, 즉 운영체제 측에서 추상화한 구조체인데요, 게다가 KPRCB는 KPCR 내에 포함된 구조체에 불과합니다. 그래서 일단 KPCR만 알아내면, KPRCB도 Prcb 필드를 이용해 확인할 수 있습니다.

7: kd> dt _KPCR ffff8100766a2000
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : 0xffff8100`766b2fb0 _KGDTENTRY64
   +0x008 TssBase          : 0xffff8100`766b1000 _KTSS64
   +0x010 UserRsp          : 0x00000047`fde7f228
   +0x018 Self             : 0xffff8100`766a2000 _KPCR
   +0x020 CurrentPrcb      : 0xffff8100`766a2180 _KPRCB
   +0x028 LockArray        : 0xffff8100`766a2870 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : 0x00000047`fdc40000 Void
   +0x038 IdtBase          : 0xffff8100`766b0000 _KIDTENTRY64
   +0x040 Unused           : [2] 0
   +0x050 Irql             : 0 ''
   +0x051 SecondLevelCacheAssociativity : 0x10 ''
   +0x052 ObsoleteNumber   : 0x7 ''
   +0x053 Fill0            : 0 ''
   +0x054 Unused0          : [3] 0
   +0x060 MajorVersion     : 1
   +0x062 MinorVersion     : 1
   +0x064 StallScaleFactor : 0xe10
   +0x068 Unused1          : [3] (null) 
   +0x080 KernelReserved   : [15] 0
   +0x0bc SecondLevelCacheSize : 0x400000
   +0x0c0 HalReserved      : [16] 0xd691cf40
   +0x100 Unused2          : 0
   +0x108 KdVersionBlock   : (null) 
   +0x110 Unused3          : (null) 
   +0x118 PcrAlign1        : [24] 0
   +0x180 Prcb             : _KPRCB

// dt _KPRCB ffff8100766a2000+0x180
// dt _KPRCB ffff8100`766a2180

7: kd> dt _KPCR ffff8100`766a2000 Prcb.
nt!_KPCR
   +0x180 Prcb  : 
       +0x000 MxCsr            : 0x1f80
       +0x004 LegacyNumber     : 0x7 ''
       +0x005 ReservedMustBeZero : 0 ''
       +0x006 InterruptRequest : 0 ''
       +0x007 IdleHalt         : 0x1 ''
       +0x008 CurrentThread    : 0xffff9485`cf602080 _KTHREAD
       +0x010 NextThread       : (null) 
       +0x018 IdleThread       : 0xffff9485`cf602080 _KTHREAD
       +0x020 NestingLevel     : 0 ''
       +0x021 ClockOwner       : 0 ''
       +0x022 PendingTickFlags : 0 ''
       +0x022 PendingTick      : 0y0
       +0x022 PendingBackupTick : 0y0
       +0x023 IdleState        : 0 ''
       +0x024 Number           : 7
       +0x028 RspBase          : 0xfffff686`1fcbcc70
       +0x030 PrcbLock         : 0
       ...[생략]...
       +0x9eac DbgMceNestingLevel : 0
       +0x9eb0 DbgMceFlags      : 0
       +0x9eb4 PrcbPad139b      : 0
       +0x9eb8 CacheProcessorSet : [5] _KAFFINITY_EX
       +0xa3e0 PrcbPad140       : [340] 0
       +0xae80 PrcbPad140a      : [8] 0
       +0xaec0 PrcbPad141       : [512] 0
       +0xbec0 RequestMailbox   : [1] _REQUEST_MAILBOX

// 0xe10 == 3600 == 3.6GHz
7: kd> dt _KPRCB ffff8100`766a2180 MHz VendorString
nt!_KPRCB
   +0x044 MHz          : 0xe10
   +0x8990 VendorString : [13]  "AuthenticAMD"




기왕에 KPRCB까지 왔으니 그것의 필드 중에 DispatcherReadyListHead를 알아볼까요? ^^

Deep Dive into Everything - Dispatcher Database
; https://haewon83.tistory.com/m/125

위의 글에서 아주 친절하게 설명하고 있으므로 저는 그냥 그대로 실습만 해보겠습니다.

우선, windbg의 !thread 명령어로는 현재 문맥으로 지정된 CPU에 실행 중인 스레드를 확인할 수 있습니다.

// 0번 CPU에 실행 중인 스레드 확인 (전체 CPU에 실행 중인 모든 스레드를 확인하고 싶다면 "!running -it" 명령어 사용)

0: kd> !thread
THREAD fffff8071c14c700  Cid 0000.0000  Teb: 0000000000000000 Win32Thread: 0000000000000000 RUNNING on processor 0
Not impersonating
DeviceMap                 ffff9f80e7a59d50
Owning Process            fffff8071c148f40       Image:         Idle
Attached Process          ffffd707b1abe040       Image:         System
Wait Start TickCount      814672         Ticks: 1 (0:00:00:00.015)
Context Switch Count      399519         IdealProcessor: 0             
UserTime                  00:00:00.000
KernelTime                03:31:44.921
Win32 Start Address nt!KiIdleLoop (0xfffff8071b819cc0)
Stack Init fffff80718f13c70 Current fffff80718f13c00
Base fffff80718f14000 Limit fffff80718f0e000 Call 0000000000000000
Priority 0  BasePriority 0  IoPriority 0  PagePriority 5
Child-SP          RetAddr               : Args to Child                                                           : Call Site
fffff807`18f137e8 fffff807`22fc2b4c     : ffffd707`b2f61890 00000000`00000000 00000000`00000000 00000000`00000000 : nt!DbgBreakPointWithStatus
fffff807`18f137f0 ffffd707`b2f61890     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0xfffff807`22fc2b4c
fffff807`18f137f8 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0xffffd707`b2f61890

보시면, callstack과 함께 Priority == 0이 나오고, 현재 스레드의 상태를 _KTHREAD.State 필드로 확인할 수 있습니다. (당연히 !thread 명령어 자체가 실행 중인 스레드를 보여주는 것이므로 Running을 의미하는 2가 나옵니다.)

0: kd> dt _KTHREAD fffff8071c14c700 State
nt!_KTHREAD
   +0x184 State : 0x2 ''

/*
0	Init
1	Ready
2	Running
3	Standby
4	Terminate
5	Waiting
6	Transition
7	Deferred Ready
*/

반면 실행 중이 아닌, 대기 중인 스레드를 확인하고 싶다면 !ready 명령어를 사용하면 된다고 하는데요,

// Windows 10+ x64인 경우

0: kd> !ready
KSHARED_READY_QUEUE fffff8071c14b040: (00) ****------------------------------------------------------------
SharedReadyQueue fffff8071c14b040: No threads in READY state
Processor 0: No threads in READY state
Processor 1: No threads in READY state
Processor 2: No threads in READY state
Processor 3: No threads in READY state
KSHARED_READY_QUEUE ffffd707b1a97640: (00) ----****--------------------------------------------------------
SharedReadyQueue ffffd707b1a97640: No threads in READY state
Processor 4: No threads in READY state
Processor 5: No threads in READY state
Processor 6: No threads in READY state
Processor 7: No threads in READY state

절묘한 타이밍인 줄은 알 수 없지만 보는 바와 같이 Ready 상태로 대기하는 스레드가 모든 프로세서에 걸쳐 하나도 없다고 나옵니다. 저 출력이 올바른지에 대한 확인을 위해 KPRCB에 있는 32개의 고정 크기를 가진 DispatcherReadyListHead 배열을 살펴볼 수 있습니다. 가령 아래는 0번 프로세서의 DispatcherReadyListHead 목록을 보여주는데요,

0: kd> !prcb
PRCB for Processor 0 at fffff80717d8c180:
Current IRQL -- 2
Threads--  Current fffff8071c14c700 Next 0000000000000000 Idle fffff8071c14c700
Processor Index 0 Number (0, 0) GroupSetMember 1
Interrupt Count -- 001519ee
Times -- Dpc    00000004 Interrupt 0000000e 
         Kernel 000c6a5b User      000003f6 

0: kd> dt _KPRCB fffff80717d8c180 DispatcherReadyListHead
nt!_KPRCB
   +0x7f40 DispatcherReadyListHead : [32] _LIST_ENTRY [ 0xfffff807`17d940c0 - 0xfffff807`17d940c0 ]

0: kd> dt _KPRCB fffff80717d8c180 -a DispatcherReadyListHead
nt!_KPRCB
   +0x7f40 DispatcherReadyListHead : 
    [00] _LIST_ENTRY [ 0xfffff807`17d940c0 - 0xfffff807`17d940c0 ]
    [01] _LIST_ENTRY [ 0xfffff807`17d940d0 - 0xfffff807`17d940d0 ]
    [02] _LIST_ENTRY [ 0xfffff807`17d940e0 - 0xfffff807`17d940e0 ]
    [03] _LIST_ENTRY [ 0xfffff807`17d940f0 - 0xfffff807`17d940f0 ]
    ...[생략]...
    [28] _LIST_ENTRY [ 0xfffff807`17d94280 - 0xfffff807`17d94280 ]
    [29] _LIST_ENTRY [ 0xfffff807`17d94290 - 0xfffff807`17d94290 ]
    [30] _LIST_ENTRY [ 0xfffff807`17d942a0 - 0xfffff807`17d942a0 ]
    [31] _LIST_ENTRY [ 0xfffff807`17d942b0 - 0xfffff807`17d942b0 ]

첫 번째 배열의 경우 0xfffff807`17d940c0 - 0xfffff807`17d940c0으로 FLink와 BLink의 값이 같다는 점에서 ready 상태의 스레드가 없다는 것을 유추할 수 있습니다. 즉, "!ready" 명령어에서 확인한 것처럼 0번 CPU의 32개 LIST_ENTRY가 모두 비어 있다는 것이 확인됩니다.

여기서 32개인 이유는, 스레드에 대한 윈도우의 스케줄링 우선순위가 32단계로 결정돼 있기 때문입니다.

Scheduling Priorities
; https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities

The priority levels range from zero (lowest priority) to 31 (highest priority).


아마도, 저 환경이 VM이라서 그런지 모르겠지만, 어쨌든 32개의 우선순위 큐에서 ready 상태의 스레드가 하나도 없고, 저 테스트를 나머지 7개의 CPU 문맥에서 모두 실행해 봐도 같다는 것을 확인할 수 있습니다.




참고로, PCR/PRCB는 MSR(model-specific register)을 이용해서도 알아낼 수 있습니다.

MSRs
; https://wiki.osdev.org/CPU_Registers_x86-64#MSRs

Intel® 64 and IA-32 Architectures Software Developer’s Manual
 - B.1 ARCHITECTURAL MSRS (630 페이지)
https://www.intel.com/content/dam/support/us/en/documents/processors/pentium4/sb/253669.pdf

가령 IA32_GS_BASE 주소에 KPCR의 주소가 담겨 있는데요,

// 0xc0000101 == IA32_GS_BASE
// 아래는 7번 CPU가 실행 중인 프로세스/스레드 문맥의 KPRCB 주소를 출력

7: kd> rdmsr 0xc0000101
msr[c0000101] = ffff8100`766a2000

// 참고로, 원래 커널 모드의 GS base 주소를 담고 있는 MSR은 IA32_KERNEL_GS_BASE인데요, 
// 왜? IA32_GS_BASE에 KPCR의 값이 담겨 있는지는 "swapgs 명령어와 (Ring 0 커널 모드의) FS, GS Segment 레지스터" 글에서 설명합니다.

이뿐만 아니라 tsc 값도 확인할 수 있고,

4: kd> rdmsr 0x10
msr[10] = 000182b2`54a00dd5

4: kd> rdmsr 0x10
msr[10] = 000182b5`bbeaf118

Extended Feature Enable Register로 알려진 값까지 구할 수 있습니다.

// 0xc0000080 == IA32_EFER (Extended Feature Enable Register)
5: kd> rdmsr 0xc0000080
msr[c0000080] = 00000000`00000d01

5: kd> .formats d01
Evaluate expression:
  Hex:     00000000`00000d01
  Decimal: 3329
  Decimal (unsigned) : 3329
  Octal:   0000000000000000006401
  Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00001101 00000001
  Chars:   ........
  Time:    Thu Jan  1 09:55:29 1970
  Float:   low 4.66492e-042 high 0
  Double:  1.64474e-320

위의 출력 결과를 IA32_EFER 명세에 따라 해석해 보면,

1       : SCE (System Call Extensions)
0000000 : 0 (Reserved)
1       : LME (Long Mode Enable)
0       : (?)
1       : LMA (Long Mode Active)
1       : NXE (No-Execute Enable)
0       : SVME (Secure Virtual Machine Enable)
0       : LMSLE (Long Mode Segment Limit Enable)
0       : FFXSR (Fast FXSAVE/FXRSTOR)
0       : TCE (Translation Cache Extension)
16~63   : 0 (Reserved) 

0번째 비트에 해당하는 SCE가 바로 (AMD64 K6 프로세서 이상부터 지원하는) user->kernel로의 전환을 보다 고속으로 실행할 수 있게 하는 syscall/sysret 명령어에 대한 허용 여부를 가리킵니다.

즉, 이 값이 1이면 syscall로 인한 진입 주소가 c0000082에 기록돼 있어야 합니다.

5: kd> rdmsr c0000082
msr[c0000082] = fffff806`08e12000




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 12/26/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 151  152  153  154  155  156  157  [158]  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1099정성태8/15/201128228오류 유형: 132. 어느 순간 갑자기 접속이 안 되는 TFS 서버
1098정성태8/15/201150255웹: 24. 네이버는 어떻게 로그인 처리를 할까요? [2]
1097정성태8/15/201121566.NET Framework: 235. 메서드의 메타 데이터 토큰 값으로 클래스를 찾아내는 방법
1096정성태8/15/201125690디버깅 기술: 42. Watson Bucket 정보를 이용한 CLR 응용 프로그램 예외 분석 - (2)
1095정성태8/14/201126139디버깅 기술: 41. Windbg - 비정상 종료된 닷넷 프로그램의 StackTrace에서 보이는 offset 값 의미
1094정성태8/14/201130531오류 유형: 131. Fiddler가 강제 종료된 경우, 웹 사이트 방문이 안되는 현상
1093정성태7/27/201124138오류 유형: 130. Unable to connect to the Microsoft Visual Studio Remote Debugging Monitor ... Access is denied.
1092정성태7/22/201126550Team Foundation Server: 46. 코드 이외의 파일에 대해 소스 제어에서 제외시키는 방법
1091정성태7/21/201125544개발 환경 구성: 128. WP7 Emulator 실행 시 audiodg.exe의 CPU 소모율 증가 [2]
1089정성태7/18/201131118.NET Framework: 234. 왜? Button 컨트롤에는 MouseDown/MouseUp 이벤트가 발생하지 않을까요?파일 다운로드1
1088정성태7/16/201124230.NET Framework: 233. Entity Framework 4.1 - 윈도우 폰 7에서의 CodeFirst 순환 참조 문제파일 다운로드1
1087정성태7/15/201126890.NET Framework: 232. Entity Framework 4.1 - CodeFirst 개체의 직렬화 시 순환 참조 해결하는 방법 - 두 번째 이야기파일 다운로드1
1086정성태7/14/201128327.NET Framework: 231. Entity Framework 4.1 - CodeFirst 개체의 직렬화 시 순환 참조 해결하는 방법 [1]파일 다운로드1
1085정성태7/14/201128805.NET Framework: 230. Entity Framework 4.1 - Code First + WCF 서비스 시 EndpointNotFoundException 오류 - 두 번째 이야기파일 다운로드1
1084정성태7/11/201134097.NET Framework: 229. SQL 서버 - DB 테이블의 데이터 변경에 대한 알림 처리 [4]파일 다운로드1
1083정성태7/11/201128139.NET Framework: 228. Entity Framework 4.1 - Code First + WCF 서비스 시 EndpointNotFoundException 오류
1082정성태7/10/201127690.NET Framework: 227. basicHttpBinding + 사용자 정의 인증 구현 [2]파일 다운로드1
1081정성태7/9/201127011VC++: 53. Windows 7에서 gcc.exe 실행 시 Access denied 오류 [2]
1080정성태7/8/201125510웹: 23. Sysnet 웹 사이트의 HTML5 변환 기록 - 두 번째 이야기파일 다운로드1
1079정성태7/6/201129950오류 유형: 129. Hyper-V + Realtek 랜카드가 설치된 시스템의 BSOD 현상 [2]
1078정성태7/5/201137455VC++: 52. Chromium 컴파일하는 방법 [2]
1077정성태6/24/201135082.NET Framework: 226. HttpWebRequest 타입의 HaveResponse 속성 이야기파일 다운로드1
1076정성태6/23/201129235오류 유형: 128. SQL Express - User Instance 옵션을 사용한 경우 발생하는 오류 메시지 유형 2가지
1075정성태6/21/201124841VS.NET IDE: 69. 윈폰 프로젝트에서 WCF 서비스 참조할 때 Reference.cs 파일이 비어있는 경우
1074정성태6/20/201124921.NET Framework: 225. 닷넷 네트워크 라이브러리의 트레이스 기능파일 다운로드1
1073정성태6/20/201127158오류 유형: 127. Visual Studio에서 WCF 서비스의 이름 변경 시 발생할 수 있는 오류
... 151  152  153  154  155  156  157  [158]  159  160  161  162  163  164  165  ...