OpenTypeフォントの続き(12)・・・PostScriptアウトラインの更に続き

前回は位置が固定であるHeaderからGlobal Subr INDEXまで見たので残りのいくつかのデータをとっとと見て行こう。

残りのデータはオフセット経由でアクセスされる。

まずは、CharStrings INDEX。

Cff3_1_2

肝となるデータである。CharStrings INDEXにはフォントに含まれる各グリフのアウトラインを記述するプログラムが格納される。前回も述べたがこのプログラムはCharstringと呼ばれ、そのフォーマットはTop DICTのCharstringTypeキーによって指定される。MinionPro-Itの場合、Top DICTにCharstringTypeキーが含まれていないので、デフォルト値2のType2 Charstringとなる。

CharStrings INDEXのcountフィールドはフォントに含まれるグリフの数を表す。上の画像より1851個のグリフが含まれている事が分かる。また、CFFにおいて各グリフはGID(Glyph Identifier)によって識別されるが、CharStrings INDEX内の先頭のグリフから順にGIDが0、1、2・・となる。上の画像ではGIDが265のグリフが表示されている。厳密にはCFFのGIDとOpenTypeフォントの文字コードからcmapテーブルによって変換されるグリフインデックスは別物であるが、PostScriptアウトラインのOpenTypeフォントの場合、OpenTypeフォントのグリフインデックス=CFFのGIDとなる。ちなみに、CharStrings INDEXへのオフセットはフォントのTop DICTのcharstringキーの値として指定される(このオフセットはCFFフォーマットの先頭からのオフセット)。

Charset。

Cff3_2

Charsetにはグリフ(GID)と名前(SID)のマッピング情報が格納される。詳細は省略するが、例えば、先ほどのCharStrings INDEXの画像でGIDが256のグリフが表示されていたが、このグリフのSIDは上の画像より1465であることがわかる(GID 0のグリフの名前は省略されているので、[Index]列が256-1=255のエントリ)。また、String INDEXよりSIDが1465の文字列はc_kであるから、GIDが256のグリフの名前はc_kであるdashdash

Encoding。

Encodingを持ったフォントが見つからんdash。たぶん、文字コードとSIDかGIDのマッピング情報が格納される(SIDかGIDのどっちかは仕様書読んでも理解できませんsad)。

Private DICT。

Cff3_3

そのまんまであるが、Private DICTにはフォントにプライベートな情報が格納されるsweat02sweat02。Subrキーはこのフォントだけからアクセスできるサブルーチンが格納されたLocal Subr INDEXへのオフセットを表す。

Local Subr INDEX。

Cff3_4

はやくも燃え尽き気味sweat02sweat01

とりあえず、バージョンを1.2として最新版をアップロードしておきました。Type2 Charstringの解析はまだですが、とりあえず、グリフの形状が分かるようになったのでこれでいいかなと・・・

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(11)・・・PostScriptアウトラインの続き

準備ができたので今回は実際にPostScriptアウトライン形式のOpenTypeフォントを解析してみるのだが、PostScriptアウトライン形式のOpenTypeフォントってどこで無料で手に入るんだdashdash??と思ってたら、いいものが既にインストールされていた。

たいていの人が既にインストールしてあるであろう無償のPDFビューアのAdobe ReaderをインストールするといくつかPostScriptアウトライン形式のOpenTypeフォントがインストールされるのでこれを解析してみる。ということでここでは、<Adobe Readerのインストールフォルダ>\Resource\Fontフォルダにある欧文用のMinonPro-It(ファイル名MinionPro-It.otf)というOpenTypeフォントを使って話を進める(別のフォルダCIDFontに小塚明朝、小塚ゴシックという日本語用のPostScriptアウトライン形式のOpenTypeフォントがあるのだが、これらのフォントは脇においておく)。

実際に解析結果を示しながら話を進めるが、最初にAdobeというかPostScript系のフォントはある程度、歴史を知らないとなぜこういう構造や仕組みになっているかなど理解に苦しむかも知れないbearing。実際、CFFフォーマットを理解するために、その前身?のType1やCID-Keyedフォントの仕様書や更にはPostScriptの言語リファレンスまで自分はかいつまんで読むはめになったsweat02sweat01

ということで、まずは前回示したCFFフォーマットのレイアウトにおける位置が固定であるHeaderからGlobal Subr INDEXまでの解析結果を順に見ていく。

まずは、Header。

Cff2_1

文字通りヘッダ情報が格納される。Major、Minorは順にCFFフォマーットのメジャーバジョーン番号、マイナーバージョン番号を表す。

Name INDEX。

Cff2_2

Name INDEXには含まれるフォントの名前が前回説明したINDEXデータとして格納される。含まれるフォントの数はこのName INDEXのcountフィールドによって決定される。上の画像より1個のフォントが含まれていることがわかる。ちなみに、他のデータはそのままにしてこのフォントの名前の先頭文字をヌル文字(0x00)にすることで、フォントを論理削除できる。

Top DICT INDEX。

Cff2_3_2

Top DICT INDEXには、含まれるフォントのトップレベルの情報が前回説明したDICTデータとして格納される。もちろん、フォントの各情報はキー・値のペアとして表されるが、定義済みのTop DICTキーには例えば次のようなものがある。

Top DICTキー
キー
version(0) SID
Notice(1) SID
Copyright(12 0) SID
FullName(2) SID
FamilyName(3) SID
省略
FontBBox(5) 配列
省略
charset(15) 数値
Encoding(15) 数値
CharStrings(15) 数値
省略

1列目はキーの名前で、括弧内はキー自身の値つまりキー値である。例えば、フォントの完全名を指定するFullNameキーの値はSIDを表すので、このSIDを使い次のString INDEXの対応するエントリを見れば、フォントの完全名がわかる。また、フォントのバウンディングボックス(境界矩形)を指定するFontBBoxキーの値は4つの要素を持つ配列(順に矩形の左端のx座標、下端のy座標、右端のx座標、上端のy座標)である。charset、Encoding、CharStringsへのCFFフォーマットの先頭からのオフセットをそれぞれ指定するcharset、Encoding、CharStringsキーの値は単純な数値である。定義されているすべてのキーとその詳細は仕様書を参照ということでdashdash・・・・ちなみに、上の画像では、計10個のキーが定義されてるが、デフォルト値を持つキーは省略できる。

String INDEX。

Cff2_4

String INDEXには含まれるフォント間で共通のフォント名以外の文字列が格納される。ちなみに、これらの文字列はSID(String Identifier)と呼ばれる2バイトの符号無し整数によって参照される。また、一般的な文字列は標準文字列として計391個(SID::0-390まで)定義済みであるので、String INDEXに含まれる各文字列のSIDは先頭の文字列から順に、391、392、393・・・となる。上の画像より1616個の文字列が格納されていることが分かる。

Global Subr INDEX。

Cff2_5

Global Subr INDEXには各フォント間で共有されるサブルーチンが格納される。CFFフォーマットでは各グリフのアウトラインが単純なデータというより一種のプログラム(Type2 Charstringなどと呼ばれる)によって定義されるが、サブルーチンとはこのプログラムから呼び出されるサブプログラムの事である(通常のコンピュータプログラム言語におけるサブルーチンと同一の概念)。上の画像より1024個のグローバルなサブルーチンが格納されている事が分かる。各サブルーチンの中身についてはまだ解析してないので表示してない・・・

と長くなったので今回はここまでdash。残りの解析に手間取ってるsweat02ので、とりあえず、ここまでの解析に対応したT2FAnalyzerの最新版をアップロードしておきました。ダウンロードは脇のいつものリンクから。

| | コメント (5) | トラックバック (0)

OpenTypeフォントの続き(10)・・・PostScriptアウトライン

一連のOpenTypeフォントのシリーズでOpenTypeフォントは、グリフデータとしてTrueType形式のアウトラインまたはPostScript形式のアウトラインを含むことができると書いたが、今回はPostScriptアウトラインについて。

PostScriptアウトラインに関するグリフデータは第2回で述べたようにCFFテーブルに格納され、このテーブルはCFF(Compact Font Format)と呼ばれる形式のフォーマットになっているが、このCFFフォーマットについて書いてみる。

CFFフォーマットは複数のフォントを格納でき、Type1、CID-Keyedフォントよりコンパクトなバイナリ表現のフォーマットである。CFFフォーマットがコンパクトな理由は、バイナリ表現を使用したり、フォント間で共通のデータを共有したり、また、頻繁に出現するデータに対してデフォルト値を与えたりしているためである。(Type1やCID-Keyedフォントって何?って話はここでは触れない。)

ということで、まずは、CFFフォーマットで使われるデータ型から。

CFFデータ型
名前 範囲 説明
Card8 0-255 1バイト符号無し整数
Card16 0-65536 2バイト符号無し整数
OffSize 1-4 オフセットのサイズを指定する1バイト符号無し整数
Offset 可変 1,2,3または4バイトオフセット
SID 0-64999 2バイトストリングID

Card8、Card16はそれぞれ1、2バイトの符号無し整数を表すデータ型である。OffSizeは対応するOffsetで表されるデータ型のフィールドのサイズを表し、1-4の範囲の値を取る。例えば、この値が1なら対応するOffset型のフィールドのサイズは1バイトになる。順に2なら2バイト、3なら3バイト、4なら4バイトである。

CFFフォーマットのレイアウトの概要は次のようになる。

CFFレイアウト
名前 説明
Header -
Name INDEX -
Top DICT INDEX -
String INDEX -
Global Subr INDEX -
Encodings -
Charsets -
FDSelect -
CharStrings INDEX -
Font DICT INDEX -
Private DICT -
Local Subr INDEX -
Copyright and Trademark Notices -

上の表のようにCFFフォーマットはいくつかのデータの集合として定義されている。また、上の表を見てわかるようにINDEXやDICTと共通の名前を持ったデータがあるが、最初にこれらについて説明する。

ちなみに、HeaderからGlobal Subr INDEXまでの最初の5つの位置は固定であり、残りはオフセット経由でアクセスされる。

まずはINDEXデータについて。

INDEXデータは可変サイズのオブジェクトの配列であり、次のような構造になる。

INDEXデータ
名前 説明
Card16 count INDEXデータ内のオブジェクトの数
OffSize offSize オフセット配列の要素のサイズ
Offset offsets[count+1] オフセット配列(オフセット配列の直前のバイトからのオフセット)
Card8 data[varies] オブジェクトデータ

countフィールドはINDEXデータ内に含まれるオブジェクトの数を表す。offSizeフィールドは続くオフセット配列の要素のサイズを表す。offsetsフィールドは各オブジェクトへのオフセットの配列であり、INDEXデータ内の各オブジェクトへはこのオフセットを使ってアクセスする。また、このオフセット配列の要素数はINDEXデータ内に含まれるオブジェクトの数+1つまりcount+1であり、i番目のオブジェクトのサイズは

i番目のオブジェクトのサイズ=offsets[i+1] - offsets[i]

で一様に求まる。このオフセットはオフセット配列の直前のバイトからのオフセットつまりoffSizeフィールドからのオフセットを表すので、オフセット配列の最初の要素offsets[0]は常に1になる。ちなみに、countフィールドが0の空のINDEXの場合、残りのフィールドは存在しない。

例えば、各フォント間で共有される文字列はString INDEXに上記の構造においてオブジェクトとして格納されている。同様に、フォント毎の情報は次に説明するDICTデータとして表現されるが、これらの情報はTop DICT INDEXに上記の構造においてオブジェクトとして格納されている(ややこしいが、Top DICT INDEX全体は上記のINDEXデータでINDEXデータ内に含まれる各オブジェクトが次に説明するDICTデータである)。

次はDICTデータについて。

DICT(DICTionary)データはキー・値のペアのコレクションであり、キーは1または2バイトのデータ、値は可変長の整数または実数を表すデータである(ちなみに、値には複数個の整数や実数の配列としても格納できる)。

DICTデータの各キー・値のペアは特別なルールでエンコードされていて、ここでは、詳細については省略するが、簡単に説明すると

値を表すバイトデータ、キーを表すバイトデータ、値を表すバイトデータ、キーを表すバイトデータ・・・・・

のようにエンコードされる。キー・値のペアの値のデータが先にくる。また、(先頭の)バイトデータによって値とキーが区別できるようになっているので、DICTデータの先頭から1バイトずつ処理すればデコードできるようになっている。例えば、DICTデータをデコードするプログラムは次のようになる。

具体的には先頭バイトが0-21ならキー、28、29、247-254なら整数の値、30なら実数の値という具合になっているdash。上記のプログラムは1つのキー・値ペアをデコードするプログラムであるが、通常、DICTデータへのオフセットと共にDICTデータ全体のサイズが与えられるので、与えられたサイズに達するまでデコードすれば、DICTデータに含まれるすべてのキー・値のペアをデコードできる。

と、抽象的なことばかりだとイメージが掴みにくいかもしれないので、次回以降具体的にこれらの中身を見ていこうと思う。また、次回でCFFの解析に対応したT2FAnalzyerの最新版も同時にアップロードしようと思うhappy02

| | コメント (0) | トラックバック (0)

CFF/Type2 Charstring

データベース絡みの事をやっていたのであるが、行き詰まったdashので、気分転換にちょっと本格的にT2FAnalyzerで要望のあったCFF(Compact Font Format)/Type2 Charstringの解析に向けて、仕様書を読み始めた。

Type2 Charstringというものがグリフのアウトラインを記述してるようであるが、TrueTypeアウトラインのglyfテーブルと違って、CFFアウトラインは一種のプログラムじゃんbearing。このプログラムの実行環境であるType2 Charstringインタプリタもどきみたいなの作らんとアウトラインの形状をラスタライズできそうにもないな(TrueTypeインストラクションほど複雑そうではないが・・)wobbly

うーん。

漠然とCFF/Type2の全体のイメージが掴めたので、CFF/Type2の解析部分のプログラミングを開始しましたhappy02

Cfftype2_2

| | コメント (1) | トラックバック (0)

Win32デバッグ(12)・・・SEH(Structured Exception Handling)

まぁ、どうしようか悩んだが、とりあえず、進める所まで進んでみますsad

ということで今回のお題目は例外。結論から言うとDelphiではtry-except文による例外処理にしろ、try-finally文の終了処理にしろ、WindowsのSEH(Structured Exception Handling)を使って実装されているのだが、そのSEHについて。

SEHはWindowsによって提供されている構造化例外処理のためのメカニズムのことであるが、その流れについて説明する。

まず、0による除算や不正なメモリへのアクセスなどのハードウェア例外にしろ、Win32 APIのRaiseException関数によるソフトウェア例外にしろ、スレッド内で例外が発生するとOSに制御が移る。そして、OSは例外が発生したスレッドのCPUレジスタなどのコンテキスト情報の保存など必要な処理を行った後、例外をハンドルするための例外ハンドラを検索し、呼び出す。

具体的には、まず、OSは例外が発生したスレッドのスレッド環境ブロック(TEB:Thread Environment Block)またはスレッド情報ブロック(TIB:Thread Information Block)と呼ばれる領域の先頭4バイト(at FS:[0])の値を取得する。この4バイトの値は例外ハンドラのリンクリストの先頭をポイントしているので、この値から先頭の例外ハンドラから順に呼び出していくのである(つまり、ここに例外ハンドラを登録すれば例外発生時に例外ハンドラが呼び出されるようになるのだが、通常は、Delphiにしろ、C++にしろ、言語提供の例外処理などを利用すれば、コンパイラによって登録するコードが挿入されるので、普通は自前で登録しないが・・)。

リンクリストのノードは次のようなEXCEPTION_REGISTRATION構造体になっている。

prevメンバは次のノードへのアドレス、handlerメンバーには例外ハンドラのアドレスつまりOSから呼び出されるコールバック関数のアドレスを表す。呼び出されるコールバック関数のプロトタイプは次のようになる。

第1引数はEXCEPTION_RECORD構造体のアドレスである。EXCEPTION_RECORD構造体は例外の種類を表すコードや例外発生時のアドレスなどの発生した例外に関する様々な情報が格納されているので、例外ハンドラはこれらの情報を見て例外を処理するかを決定する。Delphiのtry-except文では例外オブジェクトのクラスとexceptのon句に指定したクラス(on E: ExceptionClass doのExceptionClassの部分)を比較して判定している。

呼び出された例外ハンドラで例外を処理せず、OSに次の例外ハンドラを呼び出させる場合はExceptionSearchException(1)を返して例外ハンドラからOSにリターンする。

また、例外が発生したアドレスから例外の原因を修正(0による除算が発生した場合、除数を0以外の値に修正)するなどして、再実行する場合はExceptionContinueExecute(0)を返してOSにリターンする。Delphiではこれは行われない。

例外ハンドラで例外を処理する場合は、例外ハンドラからOSにリターンせず、通常は、Win32APIのRtlUnwindなどでアンワインド(巻き戻し)したり、スタックフレームを適切に設定し直して、処理を続行する。Delphiではexceptのon句の後に指定した処理から実行が再開される。

RtlUnwindによるアンワインドはMSDNのドキュメントみても何の事だかさっぱり理解できないと思うが、簡単に言うと、例外ハンドラのリンクリストから不必要になった例外ハンドラのノードを削除することである。また、このとき、例外ハンドラの2回目の呼び出しが行われる。この2回目の呼び出しのときに、通常は終了処理を行う。Delphiではtry-finally文のfinally句の後に指定した終了処理がこの2回目の呼び出しのときに、実行される。

とまぁ、おおざっぱに書くとこんなところであろうが、詳細は

すべて、英語で書かれているが、特に一番上の記事にはSEHについて分かりやすく、詳細に書かれているのでお勧めです。自分は頑張って読みましたdash

| | コメント (0) | トラックバック (0)

Win32デバッグ(11)・・・番外編

今回は寄り道。というより、前回までに示したコードに少し問題があった。

第1回で

  • ReadProcessMemory
  • WriteProcessMemory

を使えば、プロセスハンドルからそのプロセスのアドレス空間を読み書きできるし、また、

  • GetThreadContext
  • SetThreadContext

を使えば、スレッドハンドルからそのスレッドのコンテキスト(レジスタの値)を読み書きできる。

と書いたが、大切な事が抜けていたshock

ということで、例えば、あるスレッドが次に実行する命令を読み込むプログラムを上記のAPIを使って書いてみる。x86アキーテクチャではインストラクションポインタ(EIP)レジスタが次に実行される命令をポイントするので、例えば、次のようになる(繰り返すが、対象スレッドは停止してないとまずいと思う)。

x86命令は可変長の命令で何バイト読みこめばよいか?という問題があるが、上記のプログラムではとりあえず4バイト読み込んでいる。上記のプログラムを実際に動かすと正しく動いているように見えるが、問題があるのである。

何が問題かと言うと、ReadProcessMemory/WriteProcessMemoryの第2引数には読み書きするメモリの仮想アドレスを渡すのであり、上記のプログラムでは、EIPレジスタの値を渡しているのであるが、EIPレジスタの値は仮想アドレスの値ではないのである。EIPレジスタに格納されているのは確かにアドレスであるが、CSレジスタに格納されているセグメントセレクタによってポイントされるセグメント(コードセグメント)内の相対アドレス(segment-relative address)なのである。

ということで、仮想アドレスに変換する必要があるのであるが、そのために使うAPIがGetThreadSelectorEntryである。GetThreadSelectorEntryによって引数で指定したセグメントの仮想アドレス空間内のセグメントのベースアドレスが求まるので、それを使って正しく先ほどのプログラムを書き換えると次のようになる。

と、肝心な事を忘れたdash

ちなみに、最初に示したプログラムが動いてしまうのは、どうも、NT環境ではコードセグメントのベースアドレスが常に0になるからのようである。特にESPレジスタによってポイントされるスタックトップを読み書きするような場合、スレッド毎に異なるスタックセグメント(SSレジスタ)が割り当てられるので、なお更まずい。

| | コメント (0) | トラックバック (0)

Win32デバッグ(10)・・・泣

このネタも10回目に突入してしまったbearing

早速、前回やり残した事をやる。前回で逆アセンブラスクラスを設計したので、今回は問題となっていたデバッグ情報からルーチンの終了位置のアドレスを求める部分を逆アセンブルして求めるよう次のように書き換える。

で、再びドライバプログラムを実行してターゲットプログラムをプロファイルしてみたところ、ターゲットプログラムが正常に動作したhappy02。プロファイルしたターゲットプログラムはコンソールアプリで次の通り。

意味のないプログラムであるが、関数Calcが10回呼び出され、その結果がコンソールに表示されるだけである。また、関数Calcからは関数SumとMulが呼び出される。このターゲットプログラムのコールツリーは次のようになった。

Win32debug_10_1_3

上の画面から確かに関数Calcが呼び出され、関数Calcから関数SumとMulが呼び出されているのが分かるだが、呼び出された回数つまりヒットカウントが10ではなく1になってるのである・・・全部合計すると10回なのであるが、ノードが分かれている・・・

がーんsad

で、デバッグして原因を調べたところ、肝心な事をすっかり忘れていたsad。それは、最近の言語でたいていはサポートされている例外処理・終了処理である。Delphiでいうところのtry-except,try-finally文である。この実現のためにコンパイラが生成してるコードに対応できていなかったのである。つまり、今までの設計では、ルーチンは必ずCALL命令で呼び出されRET命令でリータンする、また、RET命令は必ずルーチン内で1箇所だけ(現状、1つのルーチンに対して開始位置と終了位置の2つだけのブレークポイントを作成してるため)という仕様?前提?が甘かったのである・・・(JMP命令なども無視してるし・・・)

はぁ。

どうしよう??

と、本来はすべてが上手くいき今回で最終回にする予定であったのだが。どうしよう。

また、ドライバプログラムも含めて動作する形でソースをアップロードする予定であったのだが、どうせおかしなコールグラフが表示されるのでやめておく。

と言う事で次回に続くかは本気で分からない・・ははは。

| | コメント (0) | トラックバック (0)

ODBCヘッダー

また、さぼってたwobbly。なんというかプログラミングに対する情熱?自体が最近なくなってきたwobbly。要するに作りたいものがなくなってきた。

そんな事はさておき、今回は以前やりかけの事がたくさんあると書いたが、その内のとりあえず公開できそうな物を公開してみる。ということで、今回はODBC(Open DataBase Connectivity)のDelphi用ヘッダーファイル。Cのヘッダーファイルを以前に頑張って移植したbearing

まぁ、今更ODBC?みたいな感があるが・・・Unicodeに対応するDelphi2009以前のDelphi用ですのであしからず。また、ODBCの関数を使う時は現状、LoadLibraryで自分でDLLをロードして下さいsad。ダウンロードはSkyDriveから。

本当はODBCのコンポーネントを作っていたのだが、まだ、公開できる状態ではないので、ヘッダーファイルだけでも・・・

ところでDelphiの最新版であるDelphi 2010が発売されたのであるが、Unicodeアプリを作れる環境がほしいので久しぶりに購入しようかなと思ってたり。でも、作りたいものがなくなってきたこともあり悩んでます。

| | コメント (0) | トラックバック (0)

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

以前の続きであるが、今回は間隔型の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もってないのでこららを試すことできないが・・・

| | コメント (0) | トラックバック (0)

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コンパイラ指令を使ってリンクすればよいはず。

| | コメント (0) | トラックバック (0)

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