Top > 製品版のご購入 << Back

(画像提供: ISFDB)


<AGNSSの逆アセンブル手順>



    <目次>
    1. コードとデータの区別が正確かどうか
      1.1. 本来はデータであるべきところがコードになっている
      1.2. 本来はコードであるべきところがデータになっている
    2. オフセット演算子とイミディエート値の区別が正確かどうか
      2.1. OBJファイル、PEファイル
      2.2. MSDOSのEXE/COM/SYSファイル、NEファイル、MEM/EXPファイル
    3. BROWの画面出力
    4. より高度な逆アセンブル
      4.1. ren コマンドのリネームファイル.renを作成する
      4.2. mod コマンドのモジュール分割ファイル.modを作成する
      4.3. cmt コマンドのコメントファイル.cmtを作成する
    5. 逆Cコンパイルへ

 AGNSSで完全な逆アセンブルソースを出力させる上で、 主要なチェックポイントになる点について解説します。
 逆アセンブル作業が成功したかどうかの主要なチェックポイントは、
    [1]コードとデータの区別が正確かどうか
    [2]オフセット演算子とイミディエート値(即値)の区別が正確かどうか
の2点です。

1.コードとデータの区別が正確かどうか

 コードとデータの区別については、 本来はデータであるべきところがコードになっている場合と、 本来はコードであるべきところがデータになっている場合の、 2つのまずいケースが考えられます。

1.1.本来はデータであるべきところがコードになっている

 本来はデータであるべきところがコードになっている場合は、 単に、コマンドファイル(例えばwww.agn)のpas 1にdat コマンドを追加して、 パス1からXSIMをやり直せば、すぐに直ります。例えば、www.agnに

pas 1
    inf "aaa.exe"
    typ exe
    rel on ;relative segment
    ...
    dat at 013c:1234 to 013c:1256
のようにdat コマンドを追加してから、

C>xsim -iv www

と、XSIMに-iオプションを指定します。dat コマンドは、 データエリアを正確に指定します。 (アドレスのseg:offのsegは、 rel コマンドをonと指定して、EXEの先頭segからの相対値で記述した方が一般的です。 rel コマンドはデフォルトがoff (絶対seg値) ですので注意してください。)
 データエリアかどうかを知るには、 ブラウザモードBROWのu コマンド(デバッガと同じ単純な逆アセンブル)が役立ちます。 コードが崩れている個所について、u コマンドで見てみれば、 コードにはさまれたデータエリアの部分はすぐ分かります。つまり、 l コマンド (l+ l++)で見ていって、 コードが崩れている個所に気づいた分だけ、datを追加してから、 一気にXSIMからやり直して、そこが直ったかどうかをBROWでチェックすれば済みます。

1.2.本来はコードであるべきところがデータになっている

 本来はコードであるべきところがデータになっている原因には、2つ考えられます。
 1つは、1.1で述べた、 本来はデータであるべきところがコードになっているために、 その不正なコードが、本来はコードであるべき正しいコードを潰してしまった場合です。 この場合は、1.1で述べた、 dat コマンドの指定を何度か追加してゆくうちにすべてきれいに直ります。
 2つめは、XSIMがそこをまだシミュレートしていない場合です。 コードであるべきところがデータになっているエリアに気付いたら、 BROWのy コマンドでDT属性のマップを見てみます。そのエリアで、00が続いていれば、 まだシミュレートしていないケースです。
 XSIMは、loo コマンドの回数だけ、デッドコードを探すループを実行しますが、 このloo コマンドのデフォルトは、入力実行ファイルサイズが300KBを越えた場合は、 6回までしかループを実行しませんので、コマンドファイルに

pas 1
    ...
    loo ffff
と、ffffを指定しておけば、XSIMはすべてのエリアをシミュレートしてくれます。 ffffを指定しても、ffffH回ループするわけでなく、 コードがそれ以上見つからない時点で終了しますので、 1MB程度のEXEでも、5分くらいでXSIMは終了します。
コードとデータの区別が正確に行われた時、 -vオプションで起動したXSIMの終了時に表示されるneutral areaの値が0になります。

2.オフセット演算子とイミディエート値の区別が正確かどうか

2.1.OBJファイル、PEファイル

 オフセット演算子とイミディエート値の区別が正確かどうかについては、 OBJファイルや、 入力実行ファイルがWindows 95/98/2000やNTの実行ファイル(PEファイル)の場合は、 コードエリアにおいてもデータエリアのデータオフセットについても、 完全に解決されますので、この問題は発生しません。従って、 1.11.2のデータエリアの指定を、 dat コマンドで機械的に指定してしまえば、 再アセンブル/リンクでオリジナルEXEと完全に同一に動作するEXEを、 再生することができる完全なアセンブルソースが出力されます。
 PEファイルや、 VxDや386のようなWindowsデバイスドライバ(LEファイル)のようなフラットモデルや、 コンパイラで作成された入力実行ファイルの場合は、 ren コマンドで、 フラットセグメントに含まれるすべてのコードセグメントやクラス名を_TEXTやCODE、 すべてのデータセグメントやクラス名を_DATAやDATA等とそれぞれ同一名で、 統一してリネームする必要があります。
 OBJファイルの場合は、セグメントやクラス名、 及びグループ名は、 すべてオリジナルの名前でインクルードファイル.INCに記述されますので、 リネームの必要はありません。

2.2.MSDOSのEXE/COM/SYSファイル、NEファイル、MEM/EXPファイル

 入力実行ファイルが、MSDOSの実行ファイル(EXE/COM/SYS)、 Windows3.0や3.1のような16ビットWindows(NEファイル)、 メモリファイル(MEM)やPharLap Dos-Extenderの実行ファイル(EXP)の場合は、 XSIMは、できるだけオフセット演算子を付けるようにシミュレートします。 それは、文字列のようなあるデータエリアが参照されている個所を、 BROWのf コマンドやDASMのクロスレファレンスのコメント; from によって直ちに知ることができるからです。
 余計なオフセット演算子をイミディエート値に戻すには、imd コマンドを指定します。 また、オフセット演算子を修正したり、 データエリアのデータオフセットを指定するには、 pas 2でofs コマンドを指定してからXSIMを(-iオプションなしで)通します。

3.BROWの画面出力

 BROWの画面出力は、 DASM(ソースジェネレータ)の出力のように再アセンブル/リンクを想定した、 完全なASMソースではありません。例えば、 ブラウザ画面をまたがるprocのendpは出力されませんし、 mod コマンドで出力ファイルをモジュール別にファイル分割した時に不可欠な、 インクルードファイル.INCも出力されません。
また、モジュール定義ファイル(.DEF)やリソースファイル(.RC)も出力されません。 これらはすべてDASM(ソースジェネレータ)が行なうからです。 しかし、XSIM(EXEシミュレータ)やBROW(ブラウザモード)を通じて、 コードとデータの区別やオフセット演算子とイミディエート値の区別といった性能を、 評価したり、本来のコードを再現させたりすることで、 基本的な逆アセンブル作業に習熟することができます。
 BROWを使用せずに、 毎回DASMでソースファイルやリストファイルを出力しても問題ありませんが、 その場合は、 先に出力ファイルをモジュール別にmod コマンドでファイル分割しておいて、 modのon/offオプションで、必要なモジュールのみ出力することもできます。

4.より高度な逆アセンブル

 基本的な逆アセンブル作業に習熟したら、 入力実行ファイルの各処理やデータの意味を調べる、 より高度な逆アセンブルに進みます。  ここでの具体的な作業は、
    [1]ren コマンドのリネームファイル.renを作成する
    [2]mod コマンドのモジュール分割ファイル.modを作成する
    [3]cmt コマンドのコメントファイル.cmtを作成する
の3つです。
 入力実行ファイルは、まれにデバッグ情報を含んでいる場合がありますから、 その場合は、[1]のリネーム作業や[2]のモジュール分割は、 BROWのd コマンドのダンプファイル出力や、 DASMやCGN2(C ソースジェネレータ2)のTDSファイルからの map/ren/mod/dccファイルの自動出力機能を使用すれば、 オリジナルと完全に同一に行えます。

4.1.ren コマンドのリネームファイル.renを作成する

 ren コマンドのリネームファイル.renを作成して、そこに、 関数名(proc名)やデータ名が分かったものから追加して行きます。 セグメント名は、 DASMの-vオプションで起動した場合に .msgファイルに出力されるセグメント名を参考に、 リネームファイルの最初に羅列しておきます。 PEファイルや、LEファイルのようなフラットモデルや、 コンパイラで作成された入力実行ファイルの場合は、 フラットセグメントに含まれるすべてのコードセグメントやクラス名を_TEXTやCODE、 すべてのデータセグメントやクラス名を_DATAやDATA等と、 それぞれ同一名で統一してリネームします。

4.2.mod コマンドのモジュール分割ファイル.modを作成する

 mod コマンドのモジュール分割ファイル.modを作成し、そこに、 モジュール名が分かったものから追加して行きます。モジュール名は、とりあえずは、 セグメント名と同じにしておきます。modファイルのon/offオプションで、DASM出力で、 不要なモジュールの出力を抑制できます。

4.3.cmt コマンドのコメントファイル.cmtを作成する

 cmt コマンドのコメントファイル.cmtを作成し、そこに、 入力実行ファイルの各処理やデータの意味が判明したものから、 コメントを追加して行きます。

5.逆Cコンパイルへ

 入力実行ファイルがWindowsの場合は、 モジュール定義ファイルのexport/import関数や、 リソースファイルのメニューやダイアログを見れば、 だいたい何をするEXEかはすぐ分かります。しかしソースリストを眺めているだけでは、 なかなか先に進めませんから、 EXEを実際にトレースしたり、実行ログファイルを出力させて、 いろいろ試しながら作業を進めます。
 例えば、CGN2(C ソースジェネレータ2)でシンボル/ソース情報を出力して、 デバッガでオリジナルEXEを直接ソースデバッグしたり、 構造が徐々に分かってきた構造体やポインタ、パラメータ、 変数の意味が判明したものから関数やデータをリネームして、後で、 逆Cコンパイラにかけるために、 DCC コマンドファイルにcstやcgl/cfu コマンドの形で記述しておいたり、 PROXYANと併用して、 WINAPI呼び出しシーケンスをリアルタイムで表示させて、 トレースしながら実行ログファイルを出力させたり、 再生EXEにダンプルーチンをリンクしてデータダンプさせたりします。
入力実行ファイルがWindowsの場合は、各処理はメッセージ起動されますので、 win_startからデバッガでトレースしていっても、どこにも到達できませんが、 PROXYAN のログファイルに表示される呼び出しアドレスあたりに、 デバッガのブレークポイントを設定しておけば、 各処理のソースリストの箇所をすぐに見つけ出すことができます。 これをメッセージ分岐のトップダウンで行なうには、 リソースファイルのメニューやダイアログに表示される各処理のIDの値が、 最重要のキーになります。 ある処理は、通常は他の多くの処理を呼び出していますが、 処理の概要を知るには、nestオプションをあまり大きくない2位に設定して、 出力したctr/cfr コマンドの呼び出しフロー図が、 自動的に生成されたフローチャートとして役立ちます。

株式会社 美利崎人 (ビリザキト)
〒201-0014 東京都狛江市東和泉1-34-19-102
TEL:03-5497-1962