この記事では、Windows デバッグ ツールでの C++ 式構文の使用について説明します。
デバッガーは、C++ 式と Microsoft Macro Assembler (MASM) 式の 2 種類の数値式を受け入れます。 これらの式はそれぞれ、入力と出力に関する独自の構文規則に従います。
各構文の種類を使用する場合の詳細については、「 式の評価 」および 「式の評価」 コマンドを参照してください。
C++ 式パーサーは、すべての形式の C++ 式構文をサポートしています。 この構文には、ポインター、浮動小数点数、配列を含むすべてのデータ型と、すべての C++ 単項演算子と 2 項演算子が含まれます。
デバッガーの [ウォッチ ] ウィンドウと [ローカル] ウィンドウでは、常に C++ 式エバリュエーターが使用されます。
次の例では、 ?? evaluate C++ 式 コマンドによって命令ポインター レジスタの値が表示されます。
0:000> ?? @eip
unsigned int 0x771e1a02
C++ sizeof
関数を使用して、構造体のサイズを決定できます。
0:000> ?? (sizeof(_TEB))
unsigned int 0x1000
式エバリュエーターを C++ に設定する
.expr choose 式エバリュエーターを使用して、既定の式エバリュエーターを表示し、C++ に変更します。
0:000> .expr
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
既定の式エバリュエーターが変更された後、 ? evaluate 式 コマンドを使用して C++ 式を表示できます。 次の例では、命令ポインター レジスタの値を表示します。
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
@eip
レジスタリファレンスの詳細については、「レジスタ構文」を参照してください。
この例では、0xDの 16 進値が eip レジスタに追加されます。
0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f
C++ 式のレジスタと擬似レジスタ
C++ 式内でレジスタと擬似レジスタを使用できます。 @ 記号は、レジスタまたは擬似レジスタの前に追加する必要があります。
エバリュエーターは式を評価し、適切なキャストを自動的に実行します。 実際のレジスタと整数値の擬似レジスタは、 ULONG64
にキャストされます。 すべてのアドレスは PUCHAR
にキャストされ、 $thread
は ETHREAD*
にキャストされ、 $proc
は EPROCESS*
にキャストされ、 $teb
は TEB*
にキャストされ、 $peb
は PEB*
にキャストされます。
次の使用例は、TEB を表示します。
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
代入演算子または副作用演算子によってレジスタまたは擬似レジスタを変更することはできません。 これらの値を変更するには、 r registers コマンドを使用する必要があります。
次の例では、擬似レジスタの値を 5 に設定し、それを表示します。
0:000> r $t0 = 5
0:000> ?? @$t0
unsigned int64 5
レジスタと擬似レジスタの詳細については、「 レジスタ構文 」および「 擬似レジスタ構文」を参照してください。
C++ 式の数値
C++ 式の数値は、別の方法で指定しない限り、10 進数として解釈されます。 16 進数の整数を指定するには、数値の前に 0x を追加します。 8 進数を指定するには、数値の前に 0 (ゼロ) を追加します。
既定のデバッガーの基数は、C++ 式の入力方法には影響しません。 C++ 式内で MASM 式を入れ子にすることを除き、バイナリ番号を直接入力することはできません。
xxxxxxxx'xxxxxxxx 形式で 16 進数の 64 ビット値を入力できます。 また、墓のアクセント (') を省略することもできます。 どちらの形式でも同じ値が生成されます。
L
、U
、およびI64
サフィックスを整数値と共に使用できます。 作成される数値の実際のサイズは、サフィックスと入力した数値によって異なります。 この解釈の詳細については、C++ 言語リファレンスを参照してください。
C++ 式エバリュエーターの 出力 では、C++ 式規則で指定されたデータ型が保持されます。 ただし、この式をコマンドの引数として使用すると、常にキャストが行われます。 たとえば、コマンド引数でアドレスとして使用するときに、整数値をポインターにキャストする必要はありません。 式の値を整数またはポインターに有効にキャストできない場合は、構文エラーが発生します。
一部の出力には 0n
(10 進数) プレフィックスを使用できますが、C++ 式の入力には使用できません。
C++ 式の文字と文字列
文字は、単一引用符 (') で囲んで入力できます。 標準の C++ エスケープ文字を使用できます。
文字列リテラルは、二重引用符 (") で囲むことで入力できます。 このような文字列内でエスケープ シーケンスとして \" を使用できます。 ただし、 文字列は式エバリュエーターには意味がありません。
C++ 式のシンボル
C++ 式では、各シンボルはその型に従って解釈されます。 シンボルが何を参照しているかに応じて、整数、データ構造、関数ポインター、またはその他のデータ型として解釈される場合があります。 C++ 式内で、変更されていないモジュール名などの C++ データ型に対応しないシンボルを使用すると、構文エラーが発生します。
シンボル名の前にモジュール名と感嘆符を追加する場合にのみ、シンボル名に重大なアクセント (') またはアポストロフィ (') を使用できます。 テンプレート名の後に < 区切り記号と > 区切り記号を追加する場合は、これらの区切り記号の間にスペースを追加できます。
シンボルがあいまいな場合は、モジュール名と感嘆符 (!) を追加するか、シンボルの前に感嘆符のみを追加できます。 シンボルがローカルであることを指定するには、モジュール名を省略し、シンボル名の前にドル記号と感嘆符 ($!) を含めます。 シンボル認識の詳細については、「 シンボルの構文とシンボルの一致」を参照してください。
C++ 式の構造体
C++ 式エバリュエーターは、擬似レジスタを適切な型にキャストします。 たとえば、 $teb
は TEB*
としてキャストされます。
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
次の例では、参照される構造体のメンバーへのポインターの使用を示す TEB 構造体のプロセス ID を表示します。
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
C++ 式の演算子
かっこを使用して優先順位規則をオーバーライドできます。
C++ 式の一部をかっこで囲み、式の前に 2 つの アット 記号 (@@) を追加すると、式は MASM 式の規則に従って解釈されます。 2 つの アット サインと開始かっこの間にスペースを追加することはできません。 この式の最後の値は、ULONG64値として C++ 式エバリュエーターに渡されます。
@@c++( ... )
または@@masm( ... )
を使用して式エバリュエーターを指定することもできます。
データ型は、C++ 言語では通常どおりに示されます。 配列 ([ ])、ポインター メンバー (->)、UDT メンバー (.)、およびクラス (::) のメンバーを示すシンボルがすべて認識されます。 代入演算子や副作用演算子など、すべての算術演算子がサポートされています。 ただし、 new
、 delete
、および throw
演算子を使用することはできません。また、関数を実際に呼び出すことはできません。
ポインターの算術演算がサポートされ、オフセットが正しくスケーリングされます。 関数ポインターにオフセットを追加できないことに注意してください。 関数ポインターにオフセットを追加する必要がある場合は、最初にオフセットを文字ポインターにキャストします。
C++ と同様に、無効なデータ型の演算子を使用すると、構文エラーが発生します。 デバッガーの C++ 式パーサーは、ほとんどの C++ コンパイラよりも少し緩やかな規則を使用しますが、すべての主要な規則が適用されます。 たとえば、整数以外の値をシフトすることはできません。
次の演算子を使用できます。 各セルの演算子は、下位セルの演算子よりも優先されます。 同じセル内の演算子は同じ優先順位であり、左から右に解析されます。
C++ と同様に、式の評価は、その値がわかったときに終了します。 この終わりにより、 ?? myPtr && *myPtr
などの式を効果的に使用できます。
参照と型キャスト
オペレーター | 意味 |
---|---|
式 // コメント | 後続のすべてのテキストを無視する |
クラス :: メンバー | クラスのメンバー |
クラス ::~メンバー | クラスのメンバー (デストラクター) |
:: 名前 | グローバル |
構造体 。 フィールド | 構造体内のフィールド |
Pointer ->Field | 参照される構造体のフィールド |
名前 [整数] | 配列の添字 |
LValue ++ | インクリメント (評価後) |
LValue -- | デクリメント (評価後) |
dynamic_cast<type>(値) | Typecast (常に実行) |
static_cast<type>(値) | Typecast (常に実行) |
reinterpret_cast<type>(値) | Typecast (常に実行) |
const_cast<type>(値) | Typecast (常に実行) |
値の操作
オペレーター | 意味 |
---|---|
(型) 価値 | Typecast (常に実行) |
sizeof値 | 式のサイズ |
sizeof( 型 ) | データ型のサイズ |
++ LValue | インクリメント (評価前) |
-- LValue | デクリメント (評価前) |
~ 価値 | ビット補数 |
! 価値 | 否定(ブール値) |
価値 | 単項マイナス |
+ 価値 | 単項プラス |
LValue | データ型のアドレス |
価値 | 参照外し |
構造体 。 ポインター | 構造体のメンバーへのポインター |
ポインター -> * ポインター | 参照される構造体のメンバーへのポインター |
算術
オペレーター | 意味 |
---|---|
バリュー値 | 掛け算 |
値 / 値 | 除法 |
値 % 値 | モジュラス |
値 + 値 | 追加 |
値 - 値 | 引き算 |
値<<値 | ビット単位の左シフト |
値>>値 | ビットごとの右シフト |
値<値 | より小さい (比較) |
値<= 値 | 以下 (比較) |
値>値 | より大きい (比較) |
値>= 値 | 以上 (比較) |
値 == 値 | 等しい (比較) |
値 != 値 | 等しくない (比較) |
値 と 値 | ビットごとの AND |
値 ^ 値 | ビットごとの XOR (排他的 OR) |
値 | 値 | ビットごとの OR |
論理積 | |
値 || 値 | 論理和 |
次の例では、擬似レジスタが次のように設定されていることを前提としています。
0:000> r $t0 = 0
0:000> r $t1 = 1
0:000> r $t2 = 2
0:000> ?? @$t1 + @$t2
unsigned int64 3
0:000> ?? @$t2/@$t1
unsigned int64 2
0:000> ?? @$t2|@$t1
unsigned int64 3
課題
オペレーター | 意味 |
---|---|
LValue = 価値 | 割り当てる |
LValue *= 価値 | 乗算 と割り当て |
LValue /= 価値 | 分割して割り当て |
LValue %= 価値 | 剰余と割り当て |
LValue += バリュー | 追加と割り当て |
LValue -= 価値 | 減算と割り当て |
LValue<<= 値 | 左にシフトして割り当てる |
LValue>>= 値 | 右にシフトして割り当てる |
LValue &= 値 | ANDとアサイン |
LValue |= 価値 | OR演算子を適用して割り当て |
LValue ^= 価値 | XOR と割り当て |
評価
オペレーター | 意味 |
---|---|
Value ? 値 : 値 | 条件付き評価 |
値 、 値 | すべての値を評価し、右端の値を除くすべての値を破棄します |
C++ 式のマクロ
C++ 式内でマクロを使用できます。 マクロの前に数字記号 (#) を追加する必要があります。
次のマクロを使用できます。 これらのマクロは、同じ名前の Microsoft Windows マクロと同じ定義を持ちます。 Windows マクロは、 Winnt.h
で定義されています。
マクロ | 戻り値 |
---|---|
#CONTAINING_RECORD(Address, Type, Field) | 構造体の型と構造体内のフィールドのアドレスを指定して、構造体のインスタンスのベース アドレスを返します。 |
#FIELD_OFFSET(型, フィールド) | 既知の構造体型の名前付きフィールドのバイト オフセットを返します。 |
#RTL_CONTAINS_FIELD(構造体,サイズ,フィールド) | 指定されたバイト サイズに目的のフィールドが含まれているかどうかを示します。 |
#RTL_FIELD_SIZE(Type, Field) | フィールドの型を必要とせずに、既知の型の構造体内のフィールドのサイズを返します。 |
#RTL_NUMBER_OF(配列) | 静的なサイズの配列内の要素の数を返します。 |
#RTL_SIZEOF_THROUGH_FIELD(Type, Field) | 指定したフィールドを含む、既知の型の構造体のサイズを返します。 |
この例では、 #FIELD_OFFSET
マクロを使用して、構造体内のフィールドへのバイト オフセットを計算します。
0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2