1日ひとつだけ強くなる

おべんきょうのーと

Python2.6でwinデバッガ構築-その0-

目次

更新予定

はじめに

ずいぶん前に下記の本を買った.

最近ようやく何言ってるかわかってきたので,(とは言ってもちゃんと理解しているかはわからない)学習したことを整理するために内容をまとめていきたいと思う.

まだ理解が不十分なところもあるため,誤りなどあればコメントなどで教えていただけると助かりますm( )m

Memo

初版は2010年5月24日発行.
購入したのは初版 第3刷

実行環境

WIndows7 32bit
Python 2.6

1章 開発環境,実行環境のセットアップ

本書のサンプルコードは基本的に32bit Windows向けであるので,仮想環境でWindows7 32bitを用意しPythonをインストールする.
本ではバージョン2.5が推奨されていたが,2.6を使用した.

本記事執筆時点では,(3章終わったくらい)特にPythonの外部ライブラリを使用していないため,pipなどはインストールしていない.

開発自体はMac上でエディタはEmacsを使用した.

2章 デバッガの基本原理

2.1 汎用レジスタ

x86 CPUにおいて,以下の8つの汎用レジスタが使用される.EAX, EDX, ECX, ESI, EDI, EBP, ESP, EBXの8つのレジスタ.この汎用レジスタは,それぞれ特定の用途向けに設計されている.

EAXレジスタ

アキュムレータとも呼ばれ,計算処理の遂行,関数の戻り値の格納に使用される.
乗除の計算はEAXレジスタでのみ可能.

まぁ,多分関数の戻り値がEAXレジスタに格納されることを覚えておけば問題ない.

EDXレジスタ

データレジスタ.EAXレジスタの拡張で,乗除計算の際にデータを保持して補助的な役割を果たす.

ECXレジスタ

カウントレジスタ.ループ処理の際に使われる.ECXレジスタはループの際に上方向ではなく,下方向にカウントする.具体的には以下の通り.

counter = 0

while counter < 10:
    print "Loop num: %d" % counter
    counter += 1

これをアセンブリに翻訳すると,ECXレジスタの値は1回目は10,2回目は9と,減っていくらしい.
これはコンパイルされたもの見たことないのでそういうもんだろうと覚えておくことにするのと,また実際に書いてみてみたいと思う.

ESIレジスタ

データ処理のための「ソースインデクス」として入力データストリームの位置を保持する.

EDIレジスタ

データ処理の結果がどこに格納されているかを示すポインタの役割を持つ.

ESPレジスタ

スタックの先頭を指すレジスタ.戻りアドレスを指す.

EBPレジスタ

呼び出しスタックの最下部を示す.

EBXレジスタ

特に用途があるわけではないらしい.

2.2 スタック

スタックはデータ構造の1つで,FILO(First in, Last out : 先入れ後出し)方式の構造になっている.イメージとしては,下部が閉じた筒みたいなのにデータを上から積み上げていく感じ.

関数呼び出しの際にはスタックに引数がpushされ,関数の実行が終了するとスタックから引数がpopされる.ESPレジスタはスタックフレームの最上部を指している.スタックは,メモリの高位アドレスから低位アドレスに向かって伸長する.アドレスの値が大きい方が高位アドレス.

2.3 デバッガイベント

デバッガはデバッグイベントを待つ永久ループ構造として作動する.デバッガイベントが呼ばれると,それに対応するイベントハンドラが呼び出され,デバッガは自身の実行を一時停止して次の指示を待つ.ここら辺は次回にまわすことにする.

2.4 ブレークポイント

デバッグ対象のプロセスを一時停止させるにはブレークポイントを設定する必要がある.プロセスを一時停止することで,その時点の変数,スタック上の引数,メモリ位置などを調べることができる.ブレークポイントには3種類あり,それぞれソフトウェアブレークポイント,ハードウェアブレークポイント,メモリブレイクポイントと呼ばれる.

ソフトウェアブレークポイント

ソフトウェアブレークポイントアセンブリのオペコードを特定部分つまり,ブレークポイントを設定したい場所のオペコード中の1バイトを割り込み3(INT 3)命令に置き換える.CPUはこのバイトに達すると処理を停止してINT 3 イベントを発生させる.デバッガは事前に設定されたブレークポイントのアドレスがEIPレジスタが現在指しているアドレスと一致するか確かめる.その後デバッガは一時的に保持しておいた本来のオペコードをそのアドレスに書き戻し,プロセス再開時にオペコードの実行が問題なく進むようにする.

ソフトウェアブレークポイントの処理において,ブレークポイントを設定するとプログラムのCRCチェックサムと呼ばれる誤り検出符号の値が変化する.これはどういうことかというと,プログラム自身がメモリ内の実行コードについてCRCチェックサムの変更を検知できるということである.
これにより,チェックサムが変更された時に自身をkillして自身がデバッグされるのを防ぐことができる.

ハードウェアブレークポイント

これを回避するためのものがハードウェアブレークポイントで,デバッガレジスタという特殊なレジスタを通してCPUレベルで設定する.デバッグレジスタは保持できるアドレスの制限があるため,必要なブレークポイントの数が少なく,デバッグ対象のソフトウェアに変更を加えることができない時に有効なである.ソフトウェアブレイクポイントがINT 3 を利用したのに対し,ハードウェアブレイクポイントでは割り込み1(INT 1)を利用する.デバッガは,各命令アドレスについて,ハードウェアブレイクポイントが有効かどうかをチェックする.その際に,ブレークポイントの設定されているメモリ領域についてアクセス違反が発生していないかをチェックする.該当アドレスがブレークレジスタに格納されていて,かつ読み取り/書き込み/実行のブレークポイント条件に合致する場合はINT 1 が引き起こされ,CPUは実行を停止する.

メモリブレークポイント

これは実際にブレークポイントではない.メモリブレークポイント設定の際に,メモリのページと呼ばれるOSが扱う最小単位のメモリ領域に対するパーミッションを変更し保護ページにすることで,そのメモリ領域に対してアクセスが発生した際に例外が発生し,CPUの実行が停止する.これによってそのメモリ領域に対してアクセスした命令を調べれば,何が試みられたのかを調べることができる.

とりあえず

この記事はこれくらいにしておく. 次回はWindowsデバッガを実際に作ってみる.

参考

ブログ書くの慣れてないのでこちらの構成を参考にさせてもらった.

killingout-n-bita.hateblo.jp