Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 205. Windbg - KPCR, KPRCB [링크 복사], [링크+제목 복사],
조회: 4585
글쓴 사람
정성태 (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)
1151정성태10/18/201122601Java: 13. 자바도 64비트에서 (2GB) OutOfMemoryException 예외가 발생할까?
1150정성태10/18/201129845.NET Framework: 250. WPF - ComboBox의 SelectionChagned 이벤트파일 다운로드1
1149정성태10/16/201125504.NET Framework: 249. WPF - d:DesignHeight 값을 구할 수 있을까?
1148정성태10/14/201131476Java: 12. 자바에서 LINQ 사용? [7]
1147정성태10/13/201127388.NET Framework: 248. 닷넷에서 지원되는 문자열 인코딩 이름 목록
1146정성태10/12/201133105.NET Framework: 247. LINQ에서의 Max 기능 구현 [10]파일 다운로드1
1144정성태10/10/201128831.NET Framework: 246. WCF - 서버 측에서의 유효한 Timeout 설정파일 다운로드1
1143정성태10/9/201134405.NET Framework: 245. ASP.NET 서버 측 코드에서 페이스북 계정 연동하는 방법
1142정성태10/8/201134879.NET Framework: 244. 윈도우 폼을 열고 닫는 것만으로 메모리 leak이 발생할까? [2]파일 다운로드1
1141정성태10/7/201133502.NET Framework: 243. DataTable에 대해서 Dispose 메서드를 호출할 필요가 있을까? [4]파일 다운로드1
1140정성태10/6/201126673.NET Framework: 242. 닷넷 개발자 입장에서 이해해 보는 자바의 서블릿, JSP
1138정성태10/1/201144345Java: 11. 웹 로직에서 MS-SQL 서버 연결 [2]
1137정성태9/30/201129868Java: 10. 닷넷 개발자가 설치해 본 Oracle WebLogic Server - 설치 및 기본 도메인 구성
1136정성태9/29/201125288개발 환경 구성: 131. Visual Studio - ASP.NET의 Code-behind처럼 cs 파일을 그룹핑하는 매크로 함수 [2]파일 다운로드1
1135정성태9/29/201122745오류 유형: 138. TF10216: Team Foundation services are currently unavailable
1134정성태9/27/201130216.NET Framework: 241. C# 5.0에 새로 추가된 Caller Info 특성 [5]
1133정성태9/25/201133518VC++: 54. C++로 만든 WinRT 프로그램 [2]
1132정성태9/24/201172982Java: 9. 자바의 keytool.exe 사용법과 Tomcat의 SSL 통신 설정
1131정성태9/23/201129195Java: 8. 닷넷 개발자가 구현해 본 자바 웹 서비스 (2)
1130정성태9/23/201137160Java: 7. 닷넷 개발자가 구현해 본 자바 웹 서비스 (1)파일 다운로드2
1129정성태9/22/201128826개발 환경 구성: 130. Hyper-V에 MS-DOS VM 만드는 방법 - MSDN 구독자 대상 [3]
1128정성태9/20/201128971오류 유형: 137. KB2449742 보안 업데이트로 인한 충돌 문제 해결 - 두 번째 이야기
1127정성태9/19/201133032Java: 6. Java에서 MySQL 사용 [2]
1126정성태9/18/201128204Math: 3. "유클리드 호제법"과 "Bezout's identity" 구현 코드(C#)파일 다운로드1
1125정성태9/17/201126092Windows: 54. Windows 8 개발자 Preview를 사용해 보고... [2]
1124정성태9/17/201126404.NET Framework: 240. System.Collections.ArrayList가 .NET 4.5에서 지원이 안된다??? [2]
... 151  152  153  154  155  [156]  157  158  159  160  161  162  163  164  165  ...