디버거는 특정 값을 저장하는 여러 가상 레지스터를 지원합니다.
디버거는 자동 의사 레지스터를 특정 유용한 값으로 설정합니다. 사용자 정의 유사 레지스터는 읽거나 쓸 수 있는 정수 변수입니다.
모든 의사 레지스터는 달러 기호($)로 시작합니다. MASM 구문을 사용하는 경우 달러 기호 앞에 at sign( @ )을 추가할 수 있습니다. 이 기호는 다음 토큰이 기호가 아닌 레지스터 또는 의사 레지스터임을 디버거에 알릴 수 있습니다. at 기호를 생략하면 전체 기호 테이블을 검색해야 하므로 디버거가 더 느리게 응답합니다.
예를 들어 다음 두 명령은 동일한 출력을 생성하지만 두 번째 명령은 더 빠릅니다.
0:000> ? $exp
Evaluate expression: 143 = 0000008f
0:000> ? @$exp
Evaluate expression: 143 = 0000008f
의사 레지스터와 이름이 같은 기호가 있다면, at 기호를 추가해야 합니다.
C++ 식 구문을 사용하는 경우 항상 at sign( @ )이 필요합니다.
r(Registers) 명령은 이 규칙의 예외입니다. 디버거는 항상 첫 번째 인수를 레지스터 또는 의사 레지스터로 해석합니다. (At 기호는 필요하지 않거나 허용되지 않습니다.) r 명령에 대한 두 번째 인수가 있는 경우 기본 식 구문에 따라 해석됩니다. 기본 식 구문이 C++인 경우 다음 명령을 사용하여 $t2 의사 레지스터를 $t1 의사 레지스터에 복사해야 합니다.
0:000> r $t1 = @$t2
자동 Pseudo-Registers
디버거는 다음 의사 레지스터를 자동으로 설정합니다.
| 의사 레지스터 | 설명 |
|---|---|
$ea |
실행된 마지막 명령의 유효 주소입니다. 이 명령에 유효 주소가 없으면 디버거에 "잘못된 레지스터 오류"가 표시됩니다. 이 명령에 두 개의 유효 주소가 있는 경우 디버거는 첫 번째 주소를 표시합니다. |
$ea 2 |
실행된 마지막 명령의 두 번째 유효 주소입니다. 이 명령에 두 개의 유효 주소가 없으면 디버거에 "잘못된 레지스터 오류"가 표시됩니다. |
$exp |
계산된 마지막 식입니다. |
$ra |
현재 스택에 있는 반환 주소입니다. 이 주소는 실행 명령에서 특히 유용합니다. 예를 들어 g @$ra 반환 주소가 발견될 때까지 계속됩니다( gu(Go Up) 는 현재 함수의 "스테핑 아웃"을 보다 정확하게 수행하는 방법이지만). |
$ip |
명령 포인터 레지스터입니다. x86 기반 프로세서:eip와 동일합니다. Itanium 기반 프로세서:iip과 관련이 있습니다. (자세한 내용은 이 표 다음 참고 사항을 참조하세요.) x64 기반 프로세서:rip과 동일합니다. |
$eventip |
현재 이벤트 시의 명령 포인터입니다. 이 포인터는 일반적으로 스레드를 전환하거나 명령 포인터의 값을 수동으로 변경하지 않는 한 $ip 일치합니다. |
$previp |
이전 이벤트 당시의 명령 포인터입니다. (디버거에 침입하는 것은 이벤트로 계산됩니다.) |
$relip |
현재 이벤트와 관련된 명령 포인터입니다. 분기 추적을 수행할 때 이 포인터는 분기 원본에 대한 포인터입니다. |
$scopeip |
현재 로컬 컨텍스트 ( 범위라고도 함)에 대한 명령 포인터입니다. |
$exentry |
현재 프로세스의 첫 번째 실행 파일 진입점 주소입니다. |
$retreg |
기본 반환 값 레지스터입니다. x86 기반 프로세서:eax와 동일합니다. Itanium 기반 프로세서:ret0과 동일합니다. x64 기반 프로세서:rax와 동일합니다. |
$retreg 64 |
64비트 형식의 기본 반환 값 레지스터입니다. x86 프로세서:edx:eax 쌍과 동일합니다. |
$csp |
현재 호출 스택 포인터입니다. 이 포인터는 호출 스택 깊이를 가장 많이 나타내는 레지스터입니다. x86 기반 프로세서:esp와 동일합니다. Itanium 기반 프로세서:bsp와 동일합니다. x64 기반 프로세서:rsp와 동일합니다. |
$p |
마지막 d*(메모리 표시) 명령이 인쇄한 값입니다. |
$proc |
현재 프로세스의 주소(즉, EPROCESS 블록의 주소)입니다. |
$thread |
현재 스레드의 주소입니다. 커널 모드 디버깅에서 이 주소는 ETHREAD 블록의 주소입니다. 사용자 모드 디버깅에서 이 주소는 TEB(스레드 환경 블록)의 주소입니다. |
$peb |
현재 프로세스의 PEB(프로세스 환경 블록)의 주소입니다. |
$teb |
현재 스레드의 TEB(스레드 환경 블록)의 주소입니다. |
$tpid |
현재 스레드를 소유하는 프로세스의 PID(프로세스 ID)입니다. |
$tid |
현재 스레드의 스레드 ID입니다. |
$dtid |
|
$dpid |
|
$dsid |
|
$bp번호 |
해당 중단점의 주소입니다. 예를 들어 $bp 3 (또는 $bp 03)은 중단점 ID가 3인 중단점을 참조합니다. 숫자는 항상 10진수입니다. 중단점에 숫자 ID가 없으면 $bp번호 가 0으로 계산됩니다. 중단점에 대한 자세한 내용은 중단점 사용을 참조하세요. |
$frame |
현재 프레임 인덱스입니다. 이 인덱스는 .frame(로컬 컨텍스트 설정) 명령에서 사용하는 것과 동일한 프레임 번호입니다. |
$dbgtime |
디버거가 실행 중인 컴퓨터에 따라 현재 시간입니다. |
$callret |
.call(호출 함수)이 호출되거나 .fnret /s 명령에 사용되는 마지막 함수의 반환 값입니다. $callret 데이터 형식은 이 반환 값의 데이터 형식입니다. |
$extret |
|
$extin |
|
$clrex |
|
$lastclrex |
관리되는 디버깅만: 마지막으로 발견된 CLR(공용 언어 런타임) 예외 개체의 주소입니다. |
$ptrsize |
포인터의 크기입니다. 커널 모드에서 이 크기는 대상 컴퓨터의 포인터 크기입니다. |
$pagesize |
메모리의 한 페이지에 있는 바이트 수입니다. 커널 모드에서 이 크기는 대상 컴퓨터의 페이지 크기입니다. |
$pcr |
|
$pcrb |
|
$argreg |
|
$exr_chance |
현재 예외 레코드의 발생 가능성입니다. |
$exr_code |
현재 예외 레코드에 대한 예외 코드입니다. |
$exr_numparams |
현재 예외 레코드의 매개 변수 수입니다. |
$exr_param0 |
현재 예외 레코드의 매개 변수 0 값입니다. |
$exr_param1 |
현재 예외 레코드의 매개 변수 1 값입니다. |
$exr_param2 |
현재 예외 레코드의 매개 변수 2 값입니다. |
$exr_param3 |
현재 예외 레코드의 매개 변수 3 값입니다. |
$exr_param4 |
현재 예외 레코드의 매개 변수 4 값입니다. |
$exr_param5 |
현재 예외 레코드의 매개 변수 5 값입니다. |
$exr_param6 |
현재 예외 레코드의 매개 변수 6 값입니다. |
$exr_param7 |
현재 예외 레코드의 매개 변수 7 값입니다. |
$exr_param8 |
현재 예외 레코드의 매개 변수 8 값입니다. |
$exr_param9 |
현재 예외 레코드의 매개 변수 9 값입니다. |
$exr_param10 |
현재 예외 레코드의 매개 변수 10 값입니다. |
$exr_param11 |
현재 예외 레코드의 매개 변수 11 값입니다. |
$exr_param12 |
현재 예외 레코드의 매개 변수 12 값입니다. |
$exr_param13 |
현재 예외 레코드의 매개 변수 13 값입니다. |
$exr_param14 |
현재 예외 레코드의 매개 변수 14 값입니다. |
$bug_code |
버그 확인이 발생한 경우 버그 코드입니다. 라이브 커널 모드 디버깅 및 커널 크래시 덤프에 적용됩니다. |
$bug_param1 |
버그 검사가 발생한 경우 매개 변수 1의 값입니다. 라이브 커널 모드 디버깅 및 커널 크래시 덤프에 적용됩니다. |
$bug_param2 |
버그 검사가 발생한 경우 매개 변수 2의 값입니다. 라이브 커널 모드 디버깅 및 커널 크래시 덤프에 적용됩니다. |
$bug_param3 |
버그 검사가 발생한 경우 매개 변수 3의 값입니다. 라이브 커널 모드 디버깅 및 커널 크래시 덤프에 적용됩니다. |
$bug_param4 |
버그 검사가 발생한 경우 매개 변수 4의 값입니다. 라이브 커널 모드 디버깅 및 커널 크래시 덤프에 적용됩니다. |
이러한 의사 레지스터 중 일부는 특정한 디버깅 시나리오에서 사용할 수 없을 수 있습니다. 예를 들어 사용자 모드 미니덤프 또는 특정 커널 모드 덤프 파일을 디버깅할 때는 $peb,$tid 및 $tpid 사용할 수 없습니다. ~(스레드 상태)에서 스레드 정보를 학습할 수 있지만 $tid 학습할 수 없는 상황이 있습니다. 첫 번째 디버거 이벤트에서는 $previp 의사 레지스터를 사용할 수 없습니다. 분기 추적하지 않는 한 $relip 의사 등록을 사용할 수 없습니다. 사용할 수 없는 가상 레지스터를 사용하는 경우 구문 오류가 발생합니다.
의사 레지스터는 $thread, $proc, $teb, $peb, $lastclrex와 같은 구조체의 주소를 보유하며 C++ 식 평가기에서는 적절한 데이터 유형에 따라 평가되지만 MASM 식 평가기에서는 그렇지 않습니다. 예를 들어 ? $teb 명령은 TEB의 주소를 표시하고 , ?? @$teb 명령은 전체 TEB 구조를 표시합니다. 자세한 내용은 식 평가를 확인하세요.
Itanium 기반 프로세서에서 iip 레지스터는 번들 정렬됩니다. 즉, 다른 슬롯이 실행되는 경우에도 현재 명령이 포함된 번들에서 슬롯 0을 가리킵니다. 따라서 iip 은 전체 명령 포인터가 아닙니다. $ip 의사 레지스터는 번들 및 슬롯을 포함한 실제 명령 포인터입니다. 주소 포인터에 따라 $ra, $retreg, $eventip, $previp, $relip, 및 $exentry를 보유하는 다른 의사 레지스터는 모든 프로세서에서 $ip와 동일한 구조를 가지고 있습니다.
r 명령을 사용하여 $ip 값을 변경할 수 있습니다. 이 변경은 해당 레지스터도 자동으로 변경합니다. 실행이 다시 시작되면 새 명령 포인터 주소에서 다시 시작됩니다. 이 레지스터는 수동으로 변경할 수 있는 유일한 자동 의사 레지스터입니다.
메모 MASM 구문에서 $ip 의사 레지스터를 마침표(.)로 나타낼 수 있습니다. 이 시점 전에 at 기호(@)를 추가하지 마십시오, 그리고 r 명령의 첫 번째 매개변수로 마침표를 사용해서는 안 됩니다. 이 구문은 C++ 식 내에서 허용되지 않습니다.
자동 의사 레지스터는 자동 별칭과 유사합니다. 그러나 별칭 관련 토큰(예: ${ })을 자동 별칭과 함께 사용할 수 있지만, 이러한 토큰과 함께 의사 레지스터는 사용할 수 없습니다.
User-Defined Pseudo-Registers
20개의 사용자 정의 의사 레지스터($t 0, $t 1, ..., $t 19)가 있습니다. 이러한 가상 레지스터는 디버거를 통해 읽고 쓸 수 있는 변수입니다. 이러한 가상 레지스터에 정수 값을 저장할 수 있습니다. 루프 변수로 특히 유용할 수 있습니다.
이러한 의사 레지스터 중 하나에 쓰려면 다음 예제와 같이 r(Registers) 명령을 사용합니다.
0:000> r $t0 = 7
0:000> r $t1 = 128*poi(MyVar)
모든 레지스터와 마찬가지로, 다음 예제와 같이 모든 식에서 사용자 정의 레지스터를 사용할 수 있습니다.
0:000> bp $t3
0:000> bp @$t4
0:000> ?? @$t1 + 4*@$t2
r 명령과 함께 ? 스위치를 사용하지 않는 한 의사 레지스터는 항상 정수로 입력됩니다. 이 스위치를 사용하는 경우 의사 레지스터는 할당된 값의 형식을 취합니다. 예를 들어 다음 명령은 UNICODE_STRING** 형식과 0x0012FFBC 값을 $t 15에 할당합니다.
0:000> r? $t15 = * (UNICODE_STRING*) 0x12ffbc
사용자 정의 의사 레지스터는 디버거가 시작될 때 0 값을 기본값으로 사용합니다.
참고 별칭 $u0, $u1, ..., $u9는 유사한 모양에도 불구하고 의사 레지스터가 아닙니다. 이러한 별칭에 대한 자세한 내용은 별칭 사용을 참조하세요.
예제
다음 예제에서는 현재 스레드가 NtOpenFile을 호출할 때마다 적중되는 중단점을 설정합니다. 그러나 다른 스레드가 NtOpenFile을 호출할 때 이 중단점은 적중되지 않습니다.
kd> bp /t @$thread nt!ntopenfile
예제
다음 예제에서는 레지스터가 지정된 값을 보유할 때까지 명령을 실행합니다. 먼저 "eaxstep"이라는 스크립트 파일에 조건부 단계별 단계를 위한 다음 코드를 입력합니다.
.if (@eax == 1234) { .echo 1234 } .else { t "$<eaxstep" }
다음으로, 다음 명령을 실행합니다.
t "$<eaxstep"
디버거는 단계를 수행한 다음 명령을 실행합니다. 이 경우 디버거는 1234 를 표시하거나 프로세스를 반복하는 스크립트를 실행합니다.