前回
は 、リバースエンジニアリングの関係の
始まりについて説明しました 。 もう少し時間が経ち、今ではある程度、私の研究の結果です。
.dllライブラリと.pdbデータベースからソースを復元しようとしています。 IDAを使用すると確かに結果が得られますが、満足できるものではありません。 たぶん私はただ勤勉ではありません。 したがって、私は反対に始めました-ライブラリプロジェクトのフレームワークの復元から。 私は.pdbデータベースを持っているので、非常にうまくできます。 理論的に。 理論的には、データベースはソースではなく前処理されたファイルから情報を記録するためです。 だから、あなたは取り組む必要があります。
充填
理論から話を始めましょう。 構造的には、.pdb-baseは相互接続された一連の文字(任意の変数、構造、関数、列挙、型、これらはすべて文字)です。 シンボルはタイプごとに分けられており、タイプに応じて異なるプロパティを設定できます。 プロパティを読み取ることで、構造、関数、オーバーライド、列挙、定数の説明を取得できます。これには、これらすべての関係、ファイルの名前、関数が配置されている.objモジュールなどが含まれます。 シンボルへのアクセスについては、DIA SDK(デバッグインターフェイスアクセス)があり、十分に文書化されており、その処理はそれほど難しくありません。 唯一の「問題」は、DIAがそのまま使用できるのはC / C ++のみであり、.Netで作業する場合は、インターフェイスを.Net .dllに移動して作業する必要があることですが、それは別の話です。 完成したモジュールを簡単に見つけることができます。 個人的には、Dia2Lib.dllを見つけることで2番目のオプションを選択しましたが、いくつかの関数が間違って翻訳されました(配列の代わりに、いくつかの関数のパラメーターに単純な変数を挿入しました)。
おそらく、.pdbデータベースからコードを生成するための既成のソリューションがありますが、見つかりませんでした。 そして今、私は自分で書いています。 私はC#で記述しますが、ファイルの操作の利便性は犠牲になりますが、メモリの問題は少なくなります。 最初に、キャラクターを記述するためのクラスが必要でした。 標準的なもの(Dia2Libのもの)は少し不快です。 より正確には、3自由度でデータを回転させたい場合、彼らは単にそれを我慢できません。
文字データを処理するためのクラスclass Member { public string name; public int offcet;
これらの構造体の配列に文字の通常の列挙を入力し、フレームワークの基礎を取得できます。 問題が始まった後。 最初の問題は、すでに述べたように、データベースには前処理されたファイルのすべての構造が記録されます。 たとえば次のように:
最初の例はあまり必要な構造ではありません struct /*id:2*/ _iobuf { public: char * _ptr; public: signed int _cnt; public: char * _base; public: signed int _flag; public: signed int _file; public: signed int _charbuf; public: signed int _bufsiz; public: char * _tmpfname; };
標準ライブラリの構造を使用できる人はほとんどいません。 しかし、それでも何らかの方法で追跡できる場合は、さらに悪い例があります。
2番目の例はあまり必要な構造ではありません struct /*id:24371*/ std::allocator<std::_Tree_nod<std::_Tmap_traits<int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<int>,std::allocator<std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,0> >::_Node>: std::_Allocator_base<std::_Tree_nod<std::_Tmap_traits<int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<int>,std::allocator<std::pair<int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,0> >::_Node> {
また、標準のテンプレート構造でフィルターを作成しても、翻訳中に展開または変更される多くの言語機能が残ります。 例として、カスタムテンプレートに名前を付けることができます。
テンプレートスイープの例 struct /*id:16851*/ S_BVECTOR<D3DXVECTOR2> { private: std::vector<D3DXVECTOR2,std::allocator<D3DXVECTOR2> > m_VECPath; private: signed int m_nCount; private: signed int m_nPos; public: __thiscall S_BVECTOR<struct D3DXVECTOR2>::S_BVECTOR<struct D3DXVECTOR2>(class S_BVECTOR<struct D3DXVECTOR2> const &); public: __thiscall S_BVECTOR<struct D3DXVECTOR2>::S_BVECTOR<struct D3DXVECTOR2>(void); public: void __thiscall S_BVECTOR<struct D3DXVECTOR2>::resize(unsigned short); public: __thiscall void addsize (unsigned short ); public: void __thiscall S_BVECTOR<struct D3DXVECTOR2>::setsize(unsigned short); public: __thiscall void setsizeNew (unsigned short ); public: void __thiscall S_BVECTOR<struct D3DXVECTOR2>::clear(void); public: void __thiscall S_BVECTOR<struct D3DXVECTOR2>::push_back(struct D3DXVECTOR2 &); public: __thiscall void pop_front (); public: __thiscall void pop_back (); public: int __thiscall S_BVECTOR<struct D3DXVECTOR2>::size(void); public: bool __thiscall S_BVECTOR<struct D3DXVECTOR2>::empty(void); public: __thiscall D3DXVECTOR2 * front (); public: __thiscall D3DXVECTOR2 * next (); public: __thiscall D3DXVECTOR2 * end (); public: struct D3DXVECTOR2 * __thiscall S_BVECTOR<struct D3DXVECTOR2>::operator[](int); public: __thiscall void remove (signed int ); public: __thiscall S_BVECTOR<struct D3DXVECTOR2>::~S_BVECTOR<struct D3DXVECTOR2>(void); public: __thiscall void * __vecDelDtor (unsigned int ); };
もちろん、すべてを簡単に元の形式に戻すことができます。 ただし、手動処理が必要な状況は非常に多くなります。 たとえば、使用したいライブラリの場合、2673構造がデータベースに書き込まれます。 これらのうち、実際に必要なのは約250のみで、残りはstdテンプレートスキャンおよびその他の「標準」のものです。 すべてが問題なく進むことを望むだけです。 さて、構造体に空白があると仮定します。 次に、それらをファイルに書き込む必要があります。
世代
最初に、録音用のファイル自体が必要です。 ちょっとした理論。 コンパイル時に、プリプロセッサの後のコードを含む各ソースは、コンパイラを使用してマシンコードに変換されます。 各ソースコードから、コンパイラに応じて.objファイルまたは.oファイルが取得されます。 DIA SDKを使用すると、各.objモジュールからすべてのファイルのリストを取得できます(要するに、#includeに含まれているもののリスト全体)。 ファイルのリストを取得する方法は、以前の記事で説明されていました(説明されているように...一般的
にはコードがあります )。 アマチュアの言語で言えば、各.objモジュールから、モジュールが使用されていたソースの名前(それらは同じ名前になります)および接続されたライブラリのリスト(例外がありますが、.cppを除くすべてのファイルが含まれます)を取得できます。 共通の構造を作成し、パーツをリンクしたら、構造の記録を開始できます。
私の知る限り、ソースの形式で構造が存在していたファイルの名前を直接取得することは不可能です。 しかし、構造化メソッドの実装が散在しているファイルを見つけることができます。 したがって、関数メソッドを含むすべてのファイルを単に収集し、それらからヘッダーになるファイルを選択し、そこに説明を記述し、残りのファイルをヘッダーに関連付けることをお勧めします。 しかし、メソッドが置かれているソースの名前を取得すると、それは不快であるか、バグであるか、ファイルエラーが発生している可能性があります。 名前を取得するには、まずRVA(相対仮想アドレス)でソース行のリストを見つける必要があり、次にこの行リストでこれらの行があるファイルを見つけます。 ただし、メソッドに対応する行数がゼロである場合もありますが、ファイル名はまだあります。 そして、通常、間違った名前。 これは通常、コンストラクターの分析に現れます。 たぶん、コンストラクタは単に考慮していません...
通常、そして当然のことながら、構造はheader.hとcode.cppの2つのファイルにありますが、他のオプションがあります。 たとえば、構造にはヘッダーのみが含まれているか、コードを含むファイルは拡張子.inlで表されます。または、構造は通常.pdbデータベースに従ってどこにも書き込まれません。 次のアルゴリズムを使用しました。 構造が含まれるファイルのリストにヘッダーがある場合、ヘッダーに構造を書き込み、コードがあればファイルに接続します。 構造を調べて、使用されるすべてのタイプのリストを作成します。 タイプが構造であり、そのファイルのリストがある場合は、この構造のヘッダーを接続します。そうでない場合は、この構造をファイルの先頭に書き込みます。 もう1つの不快な瞬間があります。構造は複製が非常に好きです。 それらの多くが何度も発生する理由がわかりません(実際、次から次へと多くの標準テンプレートがありますが、フィルターを有効にすると、次々に有効になります)。 さらに、そのような構造のプロパティ/メソッドは一致しますが、シリアル番号のみが異なります。 個人的には、構造体の名前の背後にある構造体で配列をソートし、すべての要素を反復処理するときに、現在の名前と前の名前を比較しました。 そしてそれは働いた。
結果
それはすべてうまくいきましたが、もちろん、私たちが望んでいるようではありません。 もちろん、それは多くのファイルを作成し、一般に、私が望むように、元のプロジェクトの構造を表示しましたが、そのような混乱があります...
主な間違い:名前空間がなく、標準テンプレート用のフィルターがなく、ライブラリ接続に置き換えられない、内部ファイル構造
がない、作成者がたわごとコードなどを
生成しない 要するに、まだ閉塞があります。 現時点ではこれですべてです。ジェネレーターコードを添付します。誰かに役立つかもしれません。
Githubアマチュアコードジェネレーターそのようなこと。