ハッキングソフトウェアは、一部の神秘的な「ハッカー」によって実行されていないことを知っていると思います。この記事の読者の大半と同じプログラマーによって実行されます。 同時に、ソフトウェア開発者自身と同じツールを使用します。 もちろん、予約については、ほとんどの場合ツールキットは非常に具体的ですが、何らかの方法でデバッガーを使用してソフトウェアを分析するためです。
私の記事のほとんどは、ソフトウェアでセキュリティを使用することに関心のある人を対象としているため、セキュリティコードの特定の部分(以前に公開されたものなど)を含む資料を送信するだけで読者を混乱させることにしました。 基本から始めて、すでに完成したベースでゆっくりと新しい素材を提供する方がはるかに簡単です。
したがって、この記事では、プログラマーの基本ツールの1つであるデバッガーについて説明します。
記事の目的:デバッガーの基本的な操作方法を検討し、その高度であまり使用されない機能を示し、例を使用してデバッガーメカニズムの動作を理解し、特定の対抗手段を検討します。
記事のボリュームは予想外に大きかったため、3つの部分に分けました。
- 最初の部分では、IDE Delphiに統合されたデバッガーの機能を検討し、最適な使用のための推奨事項と環境を構成するための一般的なヒントを示します。 このセクションの資料は、初心者の開発者と訓練を受けた専門家の両方を対象としています。
- 記事の第2部では、ソースコードの例を使用してデバッガーの動作の裏側を調べ、アプリケーションのデバッグ時にそれが使用するメカニズムを詳細に調べ、操作中にデバッガーによって行われたアプリケーションメモリ変更のオプションを示します。
- 記事の第3部では、デバッガーの実用的な使用について、特定のアンチデバッグトリックのセットを使用するアプリケーションの保護をバイパスする例として説明します。
実際に始めましょう。
1.1。 ブレークポイントの使用とローカル変数の変更
組み込みデバッガの最も一般的に使用されるツールの1つはブレークポイント(BreakPoint-以降BP)です。 BPのインストール後、プログラムはブレークポイントに到達するまで機能し、その後、作業が中断され、制御がデバッガーに転送されます。
BPをインストールおよび削除する最も簡単な方法は、ホットキー「F5」(またはメニューの「デバッグ」->「ブレークポイントの切り替え」)です。 他の方法もありますが、それらについては後で詳しく説明します。
プログラムが停止した後、アプリケーションが停止されたプロシージャのローカル変数の値を調べ、このプロシージャの呼び出しに先行する呼び出しスタックを分析できます。 ここで、これらの変数の値を変更できます。
BPを置く場所-もちろん一般的な答えはありません。 本質的に、BPは、コードの動作、その正確性がわからない、または即座に検出できないエラーを明示的に含むコードの操作の学習を促進するように設計されています。
ブレークポイントを設定し、コードの各行を順番に実行する方が、同じコードを何時間もかけて、意図したものとは異なる動作を開始する場所を見つけようとするよりもはるかに簡単です。
次の例を見てみましょう。
タスクがあります。最初に無効にされた変数の値を5倍に123ずつ増やすコードを記述し、その後、結果を10進数と16進数の形式で表示します。 予想される値は、128および00000080です。
コードがエラーで書かれているとしましょう:
var B: Integer = 123; procedure TForm1.FormCreate(Sender: TObject); var A: Integer; begin Inc(A); Inc(A); Inc(A, B); Inc(A); Inc(A); Inc(A); ShowMessage(IntToStr(A)); ShowMessage(IntToHex(A, 8)); end;
このコードは任意の値を出力しますが、変数「A」をゼロで初期化していないため、必要な値は出力しません。 また、変数「A」はローカルであるため、スタックに配置されていることを意味し、この手順の開始時にどの値を取るかを予測することはできません。 しかし、すでに営業日の終わりであり、本当に疲れて(目がぼやけている)、変数の初期化を含む行を書くのを忘れていると仮定します。
その結果、不正な値を表示するコードがあり、この動作の理由をすばやく把握したいと考えています。 BPをプロシージャの本体に入れ、プログラムを実行して実行します。

それは写真のようなものでなければなりません。 BPはラインInc(A)にインストールされます。 左下では、FormCreateプロシージャのすべてのローカル変数(ウィンドウは「ローカル変数」と呼ばれます)の値、つまりSelf変数(暗黙的に渡され、クラスメソッドに常に存在します)、Senderパラメーター、およびローカル変数「A」自体を確認できます。 その値は19079581です。「WatchList」の中央の左側は、変数「B」の値です。
両方の変数の値と完了した3行のコードを一目見ただけでも、変数 "A"の値が予想と一致しないことがわかります。 ユニットごとに2つの増分と123ずつの増分が実行されるため、変数「A」の値125が表示されるはずです。異なる値があるため、これは1つのことしか意味しません。変数「A」の初期値は本当。
仮定の正しさを確認するために、変数「A」の現在の値を正しい値に変更し、プロシージャが返す結果が予期されたものであるかどうかを確認するためにプログラムを続けましょう。
デバッガーには、変数の値を変更するための2つのツールが用意されています。
最初の「評価/変更」は、メニューまたはホットキー「Ctrl + F7」を介して呼び出されます。 これは最小限の機能を備えた非常にシンプルなツールであり、最もよく使用されます。
次のようになります。

変数の値を変更するには、「新しい値」フィールドに新しい値を指定して、「Enter」キーまたは「変更」ボタンを押します。
2番目のツールである「検査」は、「実行」メニューから、または「評価/変更」ダイアログから直接使用することもできます。 これは、より高度なパラメーターエディターです。
変数「A」の値を変更した後、ローカル変数の値のリストの変更に注意してください。

変数「A」は正しい値を取り、「F9」を押すかメニューから「実行」を選択することで、アプリケーションの実行を継続できます。 デバッガの助けを借りたこのような介入の結果、プロシージャは予想される数値128と00000080を提供します。エラーの原因を見つけ、変数「A」の正しく設定された値で実行を確認したため、プロシージャコードを安全に修正できます
検査に戻りましょう。 示された2つの呼び出し方法に加えて、右クリック時にコンテキストメニューを使用するか、ホットキー「Alt + F5」を使用して、「ローカル変数」ウィンドウで変数をダブルクリックすることでも呼び出されます。
これは、より「高度な」変数プロパティエディターですが、オブジェクトのプロパティを変更するときに使用するのが妥当です。 通常の変数を変更するのは多少不便ですが、その理由は次のとおりです。
呼び出すと、最初に次のダイアログが表示されます。

変数の説明、メモリ内の位置、および現在の値を示します。変更するには、再度省略ボタンを押す必要があります。その後、追加のウィンドウが表示されます。

通常の変数の値を変更するような単純な操作のための過度の体の動きは明らかに冗長です。 ただし、これを使用してオブジェクトのプロパティを変更すると、画像が少し変わります。
オブジェクトのプロパティへの「評価/変更」アクセスは、調査中のオブジェクトに関する情報を直接提供しないという点で多少複雑です。 たとえば、キャンバスフォームのハンドルを取得するには、次のテキストを入力する必要があります。「(SForm as TForm1).Canvas.Handle」-封印することができ、このプロパティまたはそのプロパティの名前を忘れてしまうため、多少不便です。
Inspectの場合、このような問題は発生しません。
たとえば、変数「A」ではなく、変数Selfについて「Inspect」ダイアログを開きましょう(前述のように、これはオブジェクトのすべてのメソッドに対して常に暗黙的に存在します)。

ご覧のとおり、この場合、オブジェクトのほとんどすべてのフィールドにアクセスできますが、必要に応じて変更できます。プロパティの名前が混乱することはありません。
1.2。 トレース(段階的なデバッグ)
トレースの本質は、コードの各行をステップごとに実行することです。
以前にインストールしたBPで停止し、コードを分析し、次の行に移動したいとします。 原則として、BPを配置してプログラムを実行することもできます。 そして次の、そしてそれ以降の人々のために。
実際には、プロシージャコードの各行にBPを設定して、デバッガ自体が実行できることを手動でシミュレートします(2番目のセクションで詳しく説明します)。
そして彼は次のことを知っています。- コマンド「Trace Into」(「F7」)-デバッガーは現在のコード行のコードを実行し、次のコードで停止します。 現在のコード行がプロシージャを呼び出す場合、次の行は呼び出されたプロシージャの最初の行になります。
- 「Step Over」コマンド(「F8」)は最初のコマンドに似ていますが、呼び出されたプロシージャの本体へのエントリは発生しません。
- 「次のソース行へのトレース」コマンド(「Shift + F7」)も最初のコマンドのほぼ完全な類似物ですが、「CPUビュー」ウィンドウで使用されます(このデバッグモードはこの記事では考慮されていません)。
- 「Run to Cursor」(「F4」)コマンド-デバッガーは、カーソルが現在位置している行までプログラムコードを実行します(実行中に他のBPが発生しなかったという条件で)。
- 「リターンまで実行」コマンド(「Shift + F8」)-デバッガーは、現在のプロシージャのコードを終了するまで実行します。 (多くの場合、誤って押された「F7」のカウンターとして、また実行中に他のBPが検出されないという条件下で使用されました)。
- Delphiの古いバージョンでは、「次のステートメントを設定」コマンドを使用できます。このコマンドを使用して、コードの任意の行を現在の行として設定することにより、プログラムの進行状況を変更できます。 この機能は、現在アクティブな行を指す矢印を新しい位置にドラッグできるコードエディターでも使用できます。
これらのチームは詳細な検討を必要としません。 「Trace Into」(「F7」)チームのみに注目しましょう。
たとえば、次のコードを使用します。
procedure TForm1.FormCreate(Sender: TObject); var S: TStringList; begin S := TStringList.Create; try S.Add('My value'); finally S.Free; end; end;
トレースを実行するとき、S.Add()の行にいるとき、デバッガーの反応には2つのオプションがあります。
- TStringList.Addメソッド内に移動し、
- そこには入りません。
この動作は、コンパイラの設定が原因です。 事実、Delphiにはシステムモジュール用に2セットのDCUが付属しています。 1つはデバッグ情報あり、2つ目はデバッグ情報なし。 2番目のモジュールが接続されている場合、この場合の「Trace Into」(「F7」)コマンドは「Step Over」(「F8」)として機能します。 コンパイラー設定のモジュール間の切り替えが構成されます。

また、「デバッグDCUを使用」パラメーターがこの機能を担当します。
1.3。 コンパイラー設定の詳細
コンパイラ設定のタブのオプションは、プロジェクトをビルドするときに生成されるコードに直接影響します。 このタブのいずれかの項目を変更する場合、変更を有効にするにはプロジェクトの完全な再アセンブリ(「プロジェクト>ビルド」)が必要であることを忘れないでください。 これらの設定は、さまざまな状況でのコードの動作、およびプロジェクトのデバッグ時に利用可能な情報の構成に直接影響します。
それらをより詳細に検討しましょう。
コード生成グループ
最適化パラメーターこのパラメーターは、コードの最適化に直接影響します。パラメーターがオンの場合、コードはサイズと実行速度の両方を考慮して、最適な方法で生成されます。 コードの最適化により、BPで中断した時点で既にメモリから削除されている可能性があるため、これにより、一部のローカル変数へのアクセスが(読み取りでも)アクセスできなくなる可能性があります。
例として、最初の章のコードを取り上げ、同じBPについて説明しますが、最適化はオンになっています。

ご覧のとおり、以前に使用可能なSelfおよびSender変数の値は使用できなくなりました。 また、「デバッグDCUを使用」パラメーターが無効になっているため、「コールスタック」ウィンドウで劇的な変更が発生し、以前はコールリストに関する詳細情報が表示されていました。
さらに、InspectツールはSelfオブジェクトの操作も拒否し、次のエラーを発生させます。
スタックフレームとPentiom-safe FDIVオプションこれらのパラメーターの説明はスキップします-デバッグ段階では、これらのパラメーターはおもしろくありません。 要するに、1つ目はスタックの自己分析に役立ち、2つ目は数学的コプロセッサーで作業する際のニュアンスの原因となります。 誰かがニュアンスに興味がある場合は、プロファイルでのコミュニケーションの私の座標。
レコードフィールドの位置合わせパラメーターパックされていないレコードのグローバルアラインメント。ディレクティブ「{$ Align x}」または「{$ A x}」を使用してモジュール内でローカルに変更できます。
たとえば、次のコードを検討してください。
type T = record a: Int64; b: Byte; c: Integer; d: Byte; end;
SizeOf(T)を介して取得できるこのレコードのサイズは、アライメント設定ごとに異なります。
{$ Align 1} = 14
{$ Align 2} = 16
{$ Align 4} = 20
{$ Align 8} = 24
構文オプショングループ
何も触らない方がいいです 試してみると、標準VCLでのアセンブルを拒否することもできます。
私は「Complete boolen eval」パラメータについてのみ説明します。それらのいくつかは時々それをオンにするからです。 次のコードを実行するとエラーが発生します。
function IsListDataPresent(Value: TList): Boolean; begin Result := (Value <> nil) and (Value.Count > 0); end; procedure TForm1.FormCreate(Sender: TObject); begin if IsListDataPresent(nil) then ShowMessage('boo...'); end;
この設定をオンにすると、ブール式全体がチェックされるため、最初のチェックでValueパラメーターが無効化されていると判断されたにもかかわらず、Value.Countにアクセスするとエラーが発生します。 また、(たとえば)「拡張構文」パラメーターを有効にすると、このコードは、宣言されていないResult変数について不平を言っても収集されません。
ランタイムエラーグループ
範囲確認パラメーターこれは、アプリケーションのデバッグ時に最も要求されるパラメーターの1つです。 彼は、データ配列にアクセスするときに境界をチェックする責任があります。
最も単純な場合、このコードを実行すると例外がスローされます。
const A: array [0..1] of Char = ('A', 'B'); procedure TForm1.FormCreate(Sender: TObject); var I: Integer; begin for I := 0 to 100 do Caption := Caption + A[I]; end;
ここでは、単に配列の要素にアクセスしようとしていますが、原則として、「範囲チェック」オプションを無効にして、割り当てられたメモリを超えない場合、このコードはフォームヘッダーに奇妙な行があると脅します。

これは不快ですが、プログラムの実行には重要ではありません。 ブロックに書き込もうとしたときにブロックの境界を間違えた場合、さらに悪いことになります。この場合、アプリケーションのメモリが破壊される可能性があります。
この例を検討し、最適化を無効にします。
type TMyEnum1 = (en1, en2, en3, en4, en5); TMyEnum2 = en1..en3; procedure TForm1.FormCreate(Sender: TObject); var I: TMyEnum1; HazardVariable: Integer; Buff: array [TMyEnum2] of Integer; begin HazardVariable := 100; for I := Low(I) to High(I) do Buff[I] := Integer(I); ShowMessage(IntToStr(HazardVariable)); end;
このコードの実行後、HazardVariableの数値はどうなると思いますか? 反復子のタイプを選択するときにミスをして、TMyEnum2ではなくTMyEnum1を記述したため、配列の境界の範囲を超え、スタック上のデータが失われ、それに格納されているローカル変数の値が変更されました。
最適化を有効にすると、状況はさらに悪化します。 次のエラーが表示されます。

この説明によれば、エラーテキストに記載されているアドレスはアプリケーションのメモリに属していないため、例外がどこで発生したのか、なぜ発生したのか推測することもできません。説明できません。
したがって、原則としてそれを受け入れます-アプリケーションのデバッグは、常に「範囲チェック」設定をオンにして行う必要があります!
また、このパラメーターは、変数の値を変更する際の許容値の超過を制御します。 たとえば、負の値をCardinal / DWORDなどの符号なしの型に割り当てようとした場合、またはこの型の変数が含むことができる値よりも大きい値を割り当てようとした場合、たとえばByte型の変数に500を割り当てた場合などに、例外が発生します
パラメータ「I / O cheking」Pascalスタイルのファイルを操作する場合、I / Oの結果を確認する必要があります。
このアプローチを使用するソフトウェアがまだあるかどうかはわかりませんが、Append / Assign / Rewriteなどを突然使用する場合は、アプリケーションのデバッグ時にこのオプションを有効にしてください。
オーバーフローチェックオプション算術演算の結果を制御し、結果が変数の範囲外にある場合に例外を発生させます。
このパラメーターと「範囲チェック」の違いを理解しやすくするために、次のコードを検討してください。
procedure TForm1.FormCreate(Sender: TObject); var C: Cardinal; B: Byte; I: Integer; begin I := -1; B := I; C := I; ShowMessage(IntToStr(C - B)); end;
「オーバーフローチェック」オプションが有効になっている場合、このコードは例外を発生させません。 ここでは受け入れられない値が変数に割り当てられていますが、それらに対して数学的演算は実行されません。 ただし、「範囲チェック」オプションが有効になっている場合、例外が発生します。
次に、コードの2番目のバージョンを検討します。
procedure TForm1.FormCreate(Sender: TObject); var C: Cardinal; B: Byte; begin B := 255; Inc(B); C := 0; C := C - 1; ShowMessage(IntToStr(C - B)); end;
「範囲チェック」パラメーターからの反応はなくなりますが、算術演算の結果ができないため、Inc(B)およびC:= C-1の行で「Overflow cheking」が原因のEIntegerOverflow例外が発生します。対応する変数に保存されます。
したがって、変数を使用する場合、両方のパラメーターは互いに補完します。
オーバーフローチェックは、範囲チェックほど重要ではありませんが、アプリケーションをデバッグするときは、オンのままにしておくことをお勧めします。
小さなニュアンス:暗号アルゴリズムを突然実装した場合、原則として、オーバーフロー操作は標準です。 このような状況では、コードを別のモジュールに転送し、モジュールの先頭に「{$ OVERFLOWCHECKS OFF}」ディレクティブを記述して、現在のモジュールのオーバーフローチェックを無効にします。
デバッググループ
このタブでは、すべてが非常に簡単です。 「アサーション」パラメーターを除くすべてのパラメーターは、アプリケーションの最終コードに影響を与えません。 特定のパラメータのアクティビティに応じて、各モジュールのDCUファイル内のデバッグ情報の完全性が変わります。 この情報に基づいて、デバッガーは、プログラムのアセンブラーリストをプログラマーによって実装された実際のコードと同期し、ローカル変数などを認識します。アプリケーションをコンパイルするとき、このデバッグ情報はアプリケーションの本体に収まりません。
唯一の例外は「Assertions」パラメーターです-Assert()プロシージャの操作を担当します。 このパラメーターが無効になっている場合、Assertは実行されません;それ以外の場合、実行され、そのコードもコンパイル段階でアプリケーション本体に配置されます。
まとめます。アプリケーションのデバッグの段階では、「ランタイムエラー」グループと「デバッグ」グループのすべてのパラメーターを有効にし、リリースアプリケーションの最終コンパイル中は無効にすることをお勧めします。 Delphi 7以前では、これは手動で行う必要がありますが、Delphi 2005以降では、プロジェクトビルドの通常のサポートが登場しました。このビルドでは、これらのフラグの組み合わせをアセンブリの種類ごとに個別に指定できます。
1.4。 呼び出しスタックウィンドウ
アプリケーションのデバッグ時にBPがメインツールである場合、Call Stackが2番目に重要です。
このウィンドウは次のようになります。

デバッガーがインストールされたBPでプログラムの実行を中断する(またはエラーのために停止する)前に行われた呼び出しの完全な説明が含まれています。 たとえば、スクリーンショットは、フォーム上のボタンをクリックしたときに発生した呼び出しのスタックを示しています。 TWinControl.DefaultHandlerプロシージャにWM_COMMAND(273)メッセージが到着したことから始まりました。
このリストを手元に置いて、ダブルクリックして(または「ソースを表示」メニューを使用して)コールをすばやく切り替え、各コールのローカル変数のリストを表示(「ローカルを表示」)し、コールでBPを設定します。
もちろん、多くの可能性はありませんが、ほとんどの場合、エラーが発生した場所をすばやくローカライズできるため、デバッグ中の作業が大幅に容易になります。
たとえば、EAbstractErrorエラーが発生すると、呼び出しスタックは次のようになります。

この場合、エラーがその中にある可能性が高いと言うためには、Delphiシステムモジュールにコードが配置されていない最初の呼び出しを見つけるだけで十分です。 このような呼び出しはUnit1.TForm1.Button1Click()です。これは、次のコードが実行されたButton1ボタンハンドラーです。
procedure TForm1.Button1Click(Sender: TObject); begin TStrings.Create.Add('qwe'); end;
別の使用例は、特定の関数の呼び出しをトレースすることです。 たとえば、非常に大きなアプリケーションコードがあり、その内部のどこかにMessageBoxが呼び出されていますが、このMessageBoxの呼び出しの場所をローカライズするためのヒッチのあるこの場所を見つけることはできません。 これを行うには、次の方法を使用できます。
- 対象の関数の呼び出しが宣言されているモジュール(この場合はwindows.pas)に移動します。
- その宣言(青いドット関数MessageBoxを持つ行;外部user32 ...)、
- この行にBPをインストールし、プログラムを実行します。
プログラム内の任意の場所からMessageBox呼び出しが行われるとすぐに、BPが機能し、呼び出しスタックデータに基づいて呼び出しの正確な場所を見つけることができます。
1.5。 高度なブレークポイントプロパティの操作
, , , , , , , .
. BP .

«Breakpoint list» BP.

:

«Condition» .
「パスカウント」パラメーターは、BPがアクティブになる前にそのような条件をいくつスキップする必要があるかを示し、「条件」パラメーターの値を考慮して、操作の数が最初からカウントされます。抽象的な例を考えてみましょう: procedure TForm1.FormCreate(Sender: TObject); var I, RandomValue: Integer; begin RandomValue := Random(100); for I := 1 to 100 do RandomValue := RandomValue + I; ShowMessage(IntToStr(RandomValue)); end;
, (RandomValue := ...). , 100 . «Pass count» 10. , «I» .
, 75 , «Condition»: I > 75. : , «I» 85, , 95.
:, , , . . «Pass count», , , «Pass count». ( ).
«Condition», . ., «I» 75, , . , , «Pass count» , «I» 85.
, , «I» 75, «Pass count» .
.
., , . つまり ( ), , ,
100 , , .
: (, ), .
:
procedure TForm1.FormCreate(Sender: TObject); var I, RandomValue: Integer; begin RandomValue := Random(100); for I := 1 to 10000 do RandomValue := RandomValue + I; ShowMessage(IntToStr(RandomValue)); end;
同じ7行目にBPを設定し、パラメーター「Condition」に値I = 9999を指定します。このような小さなサイクルであっても、3〜5秒の範囲で状態が機能するのを待つ必要があります。もちろん、これは便利ではありません。このような場合、次のようにコードを変更する方が簡単です。 procedure TForm1.FormCreate(Sender: TObject); var I, RandomValue: Integer; begin RandomValue := Random(100); for I := 1 to 10000 do begin RandomValue := RandomValue + I; {$IFDEF DEBUG} if I = 9999 then Beep; {$ENDIF} end; ShowMessage(IntToStr(RandomValue)); end;
… Beep, . .
( DEBUG , , , Beep-. )
«» , (SEH), Delphi try..finally..except. SEH «» . , :
function Test1(var Value: Integer): Cardinal; var I: Integer; begin Result := GetTickCount; for I := 1 to 100000000 do Inc(Value); Result := GetTickCount - Result; end; function Test2(var Value: Integer): Cardinal; var I: Integer; begin Result := GetTickCount; for I := 1 to 100000000 do try Inc(Value); finally end; Result := GetTickCount - Result; end; procedure TForm1.FormCreate(Sender: TObject); var A: Integer; begin A := 0; ShowMessage(IntToStr(Test1(A))); A := 0; ShowMessage(IntToStr(Test2(A))); end;
Test1 Test2 100 .
210 , – , – try..finally.
, «» – , …
«Group», BP . – , , , , BP, .
:

: «Enable group» – , «Disable group» – .
«Break», . , .
– .
– , .
, .
例をコンパイルする前に、コンパイラー設定で「オーバーフローチェック」オプションを有効にし、最適化を無効にしてください。 function Level3(Value: Integer): Integer; var I: Integer; begin Result := Value; for I := 0 to 9 do Inc(Result); end; function Level2(Value: Integer): Integer; var I: Integer; begin Result := Value; for I := 0 to 9 do Inc(Result, Level3(Result) shr 1); end; function Level1(Value: Integer): Integer; var I: Integer; begin Result := Value; for I := 0 to 9 do Inc(Result, Level2(Result) shr 3); end; procedure TForm1.FormCreate(Sender: TObject); begin ShowMessage(IntToStr(Level1(0))); end;
このコードを実行すると、16行目に例外が発生します Inc(Result, Level3(Result) shr 1);.
, , , , . : , , , «F9» , .
, , , , .
:- «level2BP».
- , . FormCreate ShowMessage «Disable group» «level2BP». , «Break».
- Level1 №25. , .
- , 9 ( I ). , 8 , , «level2BP». «Condition» I=8, «Break» «Enable group» «level2BP».
- , Level2, – . F9, , , , I 5. «Condition» I=5, .
- , .
— , :
rouse.drkb.ru/blog/bp3.mp4 (17 ).
( , , , , )
, , .
«Pass Count», «Condition»? , «Pass Count» . (. . «Condition») , .
.«Ignore subsequent exceptions» , .
«Handle subsequent exceptions» , .
, :
procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage('All exceptions ignored'); end; procedure TForm1.Button2Click(Sender: TObject); begin PInteger(0)^ := 123; end; procedure TForm1.Button3Click(Sender: TObject); var S: TStrings; begin S := TStrings.Create; try S.Add('abstract') finally S.Free; end; end; procedure TForm1.Button4Click(Sender: TObject); begin ShowMessage('All exceptions handled'); end;
ShowMessage , , «Break», «Ignore subsequent exceptions».
ShowMessage , «Handle subsequent exceptions».
:
- Button1
- Button2
- Button3
- Button4
- Button2
- Button3
, Button2 Button3 , 2 3 , 5 6 , Button4.
2 :«Log message» – , .
«Eval expression» – , ( «Log result») . , «123 * 2».
1.6。 «Data breakpoint», «Watch List» «Call Stack»
, , «Source Breakpoint». . . , .
, , (, , ) BP , , «Data breakpoint».
«Watch List» ( Delphi) «Breakpoint List» «Add Breakpoint->Data Breakpoint», , , , . , ( ) .
問題は、アプリケーションを起動するたびに変数が配置されるアドレスが毎回異なるため、この値の取得が非常に難しいことです。– . , , , «Data breakpoint» . , , , . . – , , , , , , . «Data breakpoint» , .
, , «Data breakpoint» – «Memory Breakpoint».
, Delphi , «Memory Breakpoint» , .
, .
:
type TTest = class Data: array [0..10] of Char; Caption: string; Description: string; procedure ShowCaption; procedure ShowDescription; procedure ShowData; end; TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private FT: TTest; procedure InitData(Value: PChar); end; var Form1: TForm1; implementation {$R *.DFM} procedure TTest.ShowCaption; begin ShowMessage(Caption); end; procedure TTest.ShowData; begin ShowMessage(PChar(@Data[0])); end; procedure TTest.ShowDescription; begin ShowMessage(Description); end; procedure TForm1.FormCreate(Sender: TObject); begin FT := TTest.Create; try FT.Caption := 'Test caption'; FT.Description := 'Test Description'; InitData(@FT.Data[0]); FT.ShowCaption; FT.ShowDescription; FT.ShowData; finally FT.Free; end; end; procedure TForm1.InitData(Value: PChar); const ValueData = 'Test data value'; var I: Integer; begin for I := 1 to Length(ValueData) do begin Value^ := ValueData[I]; Inc(Value); end; end;
, :

«Break», - «system»:

, , , «Call Stack», , ShowCaption .
BP , , , Caption, :

, - Caption. «Data breakpoint».
- Caption, FT.Description := 'Test Description';.
- , FP.Caption «Watch List» «Break When Changed». (, Delphi 2010 ), «Data breakpoint» . «Breakpoint List» «Add->Data Breakpoint», FP.Caption .
- .
, №68 – Inc(Value). «Data breakpoint» , , , , FP.Caption, – Value^ := ValueData[I].
, , . , ValueData, Data, , - , Caption Description.
1.7. 結論として
. , : , .., .
«CPU-View» Address Breakpoint. , .. , CPU-View :)
, . , BreakPoint, Data Breakpoint, Delphi, ( , TF GUARD ), Hardware Breakpoint, Delphi.
« » , aka Inovet, aka Palladin aka .
(Rouse_)
, 2012