« 2009年7月 | トップページ | 2009年9月 »

2009年8月

2009年8月23日 (日)

間隔(インターバル)型の続き

以前の続きであるが、今回は間隔型のODBCでの対応について。

ODBCではSQL92の間隔型に相当するデータ型が定義されていて、そのSQLデータ型は次のようになる。

年月間隔

  • SQL_INTERVAL_YEAR
  • SQL_INTERVAL_MONTH
  • SQL_INTERVAL_YEAR_TO_MONTH

日時間隔

  • SQL_INTERVAL_DAY
  • SQL_INTERVAL_HOUR
  • SQL_INTERVAL_MINUTE
  • SQL_INTERVAL_SECOND
  • SQL_INTERVAL_DAY_TO_HOUR
  • SQL_INTERVAL_DAY_TO_MINUTE
  • SQL_INTERVAL_DAY_TO_SECOND
  • SQL_INTERVAL_HOUR_TO_MINUTE
  • SQL_INTERVAL_HOUR_TO_SECOND
  • SQL_INTERVAL_MINUTE_TO_SECOND

これら間隔型のデータをアプリケーションから入出力する時に、Cデータ型のSQL_C_CHARにマップつまり文字列として入出力してもよいのだが、間隔型専用のSQL_C_INTERVALの接頭辞で始まるCデータ型(SQL_C_INTERVAL_YEAR等)にマップして入出力することもできる。SQL_C_INTERVALの接頭辞で始まるCデータ型はSQL_INTERVAL_STRUCT構造体として定義され、次のようになっている。

まぁ、ここらへんはODBCのドキュメントに書いてあるので・・

ここでは、ちょっとWindowsのお話でも。.NET Frameworkでは間隔を扱うTimeSpan構造体というものが定義されているが、WindowsのネイティブAPIで間隔を扱うAPIなんてものがあるのかな?と前から思っていたら偶然発見したbearing

まずは、

間隔を文字列表現に変化するAPIであるが、実際に使って10万ミリ秒を変換してみた。

Interval2_1

このAPIはShell Lightwight Utility Functionsと呼ばれるAPIで将来のWindowsでは変更または利用不可になるかもしれないとの事・・・エクスプローラでファイルをコピーすると残りXX秒とかダイアログに表示されるが、そこで使われているのだろうか??

次は、Vistaで追加された

こちらも文字列表現に変換するAPIであるが、StrFromTimeIntervalと違いロケールや書式を指定できるとの事。これに合わせて、GetLocaleInfoなどでロケールに関する様々な情報を取得・設定できるが、間隔用のフォーマットのLOCALE_SDURATION定数が追加されている。Vistaもってないのでこららを試すことできないが・・・

2009年8月15日 (土)

Win32デバッグ(9)・・・逆アセンブラ

前回で、どうもx86命令を逆アセンブルする必要が出てきたので今回は逆アセンブルについて。

まずは、逆アセンブラを自作する方向で進めていたのであるが、ご存知のようにx86命令は可変長であったり、SSE命令などの拡張命令がどんどん追加されていったり、面倒そうなので、とりあえず、既存の逆アセンブラライブラリを利用する事にした。

ということで既存の逆アセンブラライブラリをいくつか挙げてみる。

逆アセンブラライブラリ
名前 サイト ライセンス
BeaEngine http://beatrix2004.free.fr/ LGPL
diStorm64 http://www.ragestorm.net/distorm/ BSD
Hacker Disassembler Engine(HDE) http://patkov-site.narod.ru/ Free
libdisasm http://bastard.sourceforge.net/libdisasm.html Free/Open Source
SysDasm http://rootkit.com/newsread.php?newsid=208 Free/Open Source
Udis86 http://udis86.sourceforge.net/ Free/Open Source
VirtualBox Disassembler Library http://www.woodmann.com/forum/showthread.php?t=11904 ?

各逆アセンブラライブラリの詳細については、各ライブラリのサイトを参照。ここではどのライブラリを使うかだが、ここでは上記の1つであるHacker Disassembler Engine(HDE)を使うことにした。理由は最終更新日が最近(2009年3月)であるのと、軽量である点だ。他のライブラリは結構高機能で逆アセンブルした機械語をアセンブラのニーモニックのテキスト形式に変換できたりするが、今回はx86命令のRET命令を検出できればよいので、命令長やオプコードなど最小限の情報だけを取得できるHDEにした(ちなみに、上記BeaEngineのサイトにLength Disassemblerというものもあるようだ・・)。

次に、新しいクラスを設計するが、その前にx86命令の大まかな命令フォーマットを知らないと設計できないので、x86命令の命令フォーマットを少し。x86命令の命令フォーマットは次のようになる。

X86instformat_2

先程も述べたように可変長なのであるが、各フィールドの詳細はIntelのマニュアルを参照ということで。

まずは、抽象クラスで逆アセンブルを行うTDisassemblerクラス。

各プロパティは先ほどのx86命令フォーマットの各フィールドに対応する。そのうちのいくつかのプロパティの型がVariant型なのは、対応するフィールドが必ずしも存在するとは限らないからである。DoNextメソッドは現在位置からx86命令を解析し、次に命令の先頭にポインタを進める抽象メソッドであるので、下位クラスではDoNextメソッドをオーバーライドしなければいけない、また、解析が成功したらTrueを返す。

そして、TDisassemblerクラスを継承し、HDEを使った逆アセンブラクラスTHdeDisassemblerのDoNextメソッドを次のように実装する。

ここは、使用する各逆アセンブラライブラリに依存するので詳しくは省略・・

後、一息なのであるが、今回はここまでbearing

最後に、HDEはC言語のライブラリなので、Delphiで利用するためには、ヘッダを移植し、ライブラリ自体はCコンパイラでコンパイルし、Delphiの$Lコンパイラ指令を使ってリンクすればよいはず。

2009年8月14日 (金)

Win32デバッグ(8)・・・そして

今回は、前回までで必要なクラスの設計そして実装ができたので、約束通りドライバを作成して動かしてみる。

で、ドライバとTD32デバッグ情報付きの簡単なターゲットプログラムを用意して動かしてみたのだが・・・

ターゲットプログラムの動きがおかしい・・・weep

動的コールグラフを求めるにあたって、前回までに述べた通り、ブレークポイントを設定するためターゲットプログラムのプロセスのメモリ空間のコード領域を書き換えてるのだがどうもそこらへんの書き換えがおかしいっぽい・・・

で、調べる事数時間・・・

予期せぬ原因が分かったweep。やはり、ブレークポイントを設定する位置のアドレスの計算に問題があった・・・。再掲載するが現在は

のように、ルーチンの呼び出しを検出するために、ルーチンの開始位置と終了位置のアドレスをデバッグ情報から取得できるセクションインデックスと開始位置のオフセット・長さから求め、開始位置のアドレスの計算は合っているようなのだが、終了位置のアドレスの計算が間違っているようなのである。現在、終了位置のアドレスは上の25行目のように、

  終了位置のアドレス=開始位置のアドレス+ルーチンの長さ

で求めているのであるが、この式によって求めた終了位置のアドレスがルーチンによって想定外の位置を指してたりと・・・。要するに、上の式によって求めたルーチンの終了位置のアドレスは必ずx86命令のRET命令の先頭を指していると思っていたのだが、甘かった・・

そして、対応策を色々考えたのだが、どうも、まさかここまでする必要はないと思ってこのネタをやりだしたのだが、開始位置のアドレスは正しいようなので、開始位置のアドレスから自前で逆アセンブルして、x86命令のRET命令の位置を求めることに・・・(まぁ、必ずしもすべてのコンパイラがルーチンの呼び出し・リターンにx86命令のCALL・RET命令を使ったコードを生成するとは限らない?が、まぁ、そこらへんはよしとしよう・・)。

続く・・・

2009年8月13日 (木)

Win32デバッグ(7)・・・動的コールグラフ(クラス設計の続き)

今回は残りの必要なクラスをさっさと設計してまおうbearing。また、長くなりそうだが・・・

まずは、要となるクラス。動的グラフとはつまるところルーチンの呼び出し関係を表すグラフ理論?で言うところ有向グラフなのであるが、そのルーチンの呼び出しを表すノードを表すTCallGraphNodeクラスを次のように定義する。

Callerプロパティはこのノードが表すルーチンを呼び出したルーチンのノードをあらわす。

Calleesプロパティは、次に出てくるTCallGraphNodeListクラスのプロパティであるが、これはこのルーチンから呼び出されたルーチンのノードリストを表す。あるルーチンから同じルーチンが複数回呼び出される時、呼び出し毎にノードを作成しても良いがメモリ効率が悪そうなのでここでは1つにまとめることにする。

HitCountプロパティはこのノードが表すルーチンが呼び出された回数を表す(もちろん、前述のCallerプロパティのノードが表すルーチンによって呼び出された回数である)。

Symbolプロパティは呼び出されたルーチンを表す。

次に、TCallGraphNodeのリストを保持するTCallGraphNodeListクラス。

ここでは、1点だけ、先程、あるルーチン内から同じルーチンへの直接の呼び出しは1つにまとめると書いたが、そのため、このリストは同じシンボルを参照する複数のノードを追加しようとすると例外が発生するようにしてある。シンボルの等価性の判定には、ここでは単純にTSymbolクラスのインスタンスの参照を比較してるがまぁ、これで問題ないだろう・・

ふぅ。コーヒーブレイクcafe

次に、ここで定義したクラスをどう使うかであるが、対象プロセスがマルチスレッドアプリケーションの場合、その動的コールグラフってどうなるんだ??って事の解を探る気力が全くないbearing(最近、思考するのが疲れたので息抜きでブログ書いてるのであるが・・・)ので、ここでは、スレッド毎にコールグラフを作成する事にするのでTThreadクラスにいくつか修正を加えてみた。

CallGraphプロパティは動的コールグラフを表すTCallGraphクラスのプロパティである。初出のクラスであるが、これは動的コールグラフの先頭のヘッドノードのリストを管理するクラスである。なぜ、リストを管理する必要があるかと言うと、少し考えれば分かるのだが、ルーチンの呼び出し階層に現われるルーチンにすべてに対するシンボル情報が存在もしくては提供されるとは限らないので、動的コールグラフが切れる可能性があるためである。クラスの定義はここでは省略。

CallStackプロパティはコールスタックを表すスタックである。動的コールグラフを成長させる時、内部で一時的に使用する。ルーチンの実行の開始の検出時には、そのルーチンのノードをスタックにプッシュ、ルーチンの実行の終了の検出時には、スタックからノードをポップする。このようにして、現在実行されているルーチンを追跡する(スタックトップのノードのルーチンが現在実行されているルーチンになる)。まぁ、実際、スタックなんて大袈裟なものを容易しなくても、現在実行されているルーチンのノードを表す変数1個用意すればすむのだが・・・

いよいよ、終盤であるが。以上を踏まえて、実際に動的コールグラフを成長させるプログラムは例えば次のようになる。

以外にシンプルだった。以上。

次回以降は、実際に今まで定義したクラスを使うドライバプログラムを作成して実際に動かしてみようと思う。

ここまでのプログラムはいつものようにSkyDriveから。

2009年8月12日 (水)

Win32デバッグ(6)・・・動的コールグラフ(クラス設計)

今回はWin32デバッグの続きで、新たなクラスを設計してみるgood

まずは、動的コールグラフにおいて、シンボルを表すクラス。

Sectionプロパティはセクションインデックス、StartOffset・Lengthプロパティはシンボルというよりここでは関数や手続きもしくはメソッドつまりルーチンのセクション内での開始位置のオフセット・長さを表す。このクラスはDelphiの言語上厳密に、抽象クラスではないが、抽象クラス的な使い方をする。理由は後述。

お約束?通り、わざわざ専用のリストクラスを定義する必要ないかもしれないが、TSymbolクラスのリストを保持するTSymbolListクラスを次のように定義してみる。

このクラスのリストは実行を追跡するルーチンだけを保持するために使われる。つまり、コンパイラやリンカによって生成されるデバッグ情報のすべてのルーチンの実行を追跡するのではなく、必要に応じて選択できるようにする。

また、最終的なアウトプットとして動的コールグラフを画面に表示したりする必要があるのだが、その時、上記TSymbolクラスのセクションインデックスや開始位置のオフセット・長さなどを表示してもユーザーには区別できないので、ルーチンの名前など表示したりするわけだが、そのためには、例えば、TD32デバッグ情報の場合、例えば、

として、TSymbolListクラスのリストには実際にはこのクラスのインタンスを格納する事になる。デバッグ情報に格納されている情報はそれを生成したコンパイラやリンカもしくはフォーマットによって変わるので、このような設計にする。先ほどTSymbolクラスは抽象クラス的な使い方をすると書いたがこのためである(まぁ、ルーチンの名前はどのコンパイラ・リンカでも持ってると思うが・・・)。

次に、各ルーチンの実行の追跡においてブレークポイントを設定・解除する必要があるが、ここではそれ専用のクラスを設計する。まずは、1個のブレークポントを表すTBreakpointクラスを定義する。

Addressプロパティはブレークポイントのアドレスを表す、アドレスは上述したシンボルの情報を元に計算する。OrignalCodeプロパティはブレークポイントを設定する位置の元の命令コードを表す。ブレークポイントの設定では1バイトのINT 3命令(具体的には16進数の0xCC)で置き換えるので1バイトのByte型である。Symbolプロパティはブレークポイントの元になるTSymbolクラスのシンボルであり、Startプロパティはブレークポイントがその元になったシンボルの開始位置または終了位置のどちらに対応するかを表す。と、1つのTSymbolクラスのインタンスからルーチンの2つのTBreakpointクラスのインスタンスを作成する。

また、実際にブレークポイントを設定、解除するためのSetBreakpoint、ResetBreakpointメソッドをヘルパとして追加してある。実装はこんな感じ。

同じく、ブレークポイントのリストクラスであるTBreakpointListクラスを定義する。

リスト内のすべてのブレークポイントを設定・解除するSetAllBreakpointsメソッド、ResetAllBreakpointsメソッドをヘルパとして追加してある。

ふぅ。疲れたdespair

実際にこれらをどう使うかだが、デバッグ対象のプロセスのメインの実行可能モジュール(EXEファイル)内のルーチンだけ実行を追跡するならプロセスレベルのTProcessクラスでもよいのだが、前回に書いたように、DLLファイル内のルーチンの実行の追跡もできるように、TModuleクラスにTSymbolListクラスのSymbolsプロパティ、TBreakpontListクラスのBreakpointsプロパティを追加する事にする(もちろん、DLLのデバッグ情報が見つかればの話だが・・・)。

また、実際にブレークポイントを設定するために、CREATE_PROCESS_DEBUG_EVENT、LOAD_DLL_DEBUG_EVENTでブレークポイントを設定するように修正。

と長くなるので、今回はここまでで、2回にわけようと思うbearing。作ったソースは次回ダウンロードできるようにする。というより、クラス図書いた方が速いとか・・

2009年8月 8日 (土)

間隔(インターバル)型

ちょっと、データベースのデータ型のお話。

SQL92では2つの日時の差つまり期間を表すデータ型として間隔(インターバル)型が定義されているが、これらは次の2つに分類される。

  • 年月間隔(year-month間隔)
  • 日時間隔(day-time間隔)

年月間隔とは2つの日時の差を年、月で表すデータ型で例えば、「123年4ヶ月」、「48ヶ月」は年月間隔である。同様に、日時間隔は2つの日時の差を日、時、分、秒で表すデータ型で、「235日」、「64時間23分12秒」などは日時間隔である。Oracleでは年月間隔の間隔型としてINTERVAL YEAR TO MONTH型、日時間隔の間隔型としてINTERVAL DAY TO SECOND型が用意されている。DB2には残念ながら間隔型は用意されていない。

と、これら間隔型を理解するのはそう難しくないと思うが、1点注意する事がある。それは、年月間隔に属する間隔型と日時間隔に属する間隔型には互換性がないのが基本?である。つまり、年月間隔に属する間隔型と日時間隔に属する間隔型を比較したり、相互に変換できないのである。一見すると

年月間隔の「123年4ヶ月」>日時間隔の「36ヶ月」

のように比較したり、

年月間隔の「1年」->日時間隔の「365日」

のように変換できそうだが、できないのである。

なぜできないのかは、少し考えてみれば分かるように、月によって日数(28,29,30,31日)が変わったり、平年とうるう年では日数が違うからである。つまり、年月間隔の「1ヶ月」といっても1月なら31日あるし、平年の2月なら29日あるし、日時間隔の日数に変換しようとしても決定できないのである。

と当たり前の事を書いてみる。

最後に実際にOracleで年月間隔と日時間隔の間隔を比較してみる。

Interval1_1

月によって日数が変わるといっても、年月間隔の「12年」と日時間隔の「36日」じゃどう転んでも、「12年」の方が長いのであるが、年月間隔と日時間隔は互換性がないのでエラーになるのである。

« 2009年7月 | トップページ | 2009年9月 »

自作ソフトウェア

無料ブログはココログ

メモ