私は、c/c++ でポインターと関数ポインターをいじっています。関数のアドレスを取得できるように、関数呼び出しが実際に終了する場所を変更できますか?
関数のメモリ アドレスを取得してから、その場所に 2 つ目の関数アドレスを書き込もうとしましたが、アクセス違反エラーが発生しました。
よろしく、
関数ポインターは、int や double と同様に変数です。関数のアドレスは別のものです。.text
これは、バイナリのセクション内の関数の開始位置です。関数のアドレスを同じ型の関数ポインターに割り当てることはできますが、.text
セクションは読み取り専用であるため、変更することはできません。関数のアドレスへの書き込みは、関数の先頭にあるコードを上書きしようとするため、許可されていません。
注: 実行時に関数呼び出しが終了する場所を変更したい場合は、仮想ディスパッチ テーブルまたは vtable と呼ばれるものを作成できます。これは関数ポインターを含む構造体であり、ポリモーフィズムのために c++ などの言語で使用されます。
例えば:
struct VTable {
int (*foo)(void);
int (*bar)(int);
} vTbl;
実行時に、vTbl.foo
との値を変更して、vTbl.bar
別の関数を指すようにすることができます。また、呼び出しはvTbl.foo()
、.bar
新しい関数に向けられます。
呼び出そうとしている関数がインライン化されている場合は、ほとんど運が悪いことになります。ただし、インライン化されていない場合は、次の方法があります。
UnixシステムにはLD_PRELOAD
、共有ライブラリの関数を独自のバージョンでオーバーライドできる、と呼ばれるダイナミックリンカの共通機能があります。質問「LD_PRELOADトリックとは何ですか? 」を参照してください。これについての議論のために。ハイジャックしようとしている関数が共有ライブラリからロードされていない場合(つまり、実行可能ファイルの一部である場合、または静的にリンクされたライブラリからのものである場合)、運が悪い可能性があります。
Windowsには、他の攻撃ベクトルがあります。フックする関数がDLLによってエクスポートされる場合は、アドレステーブルパッチのインポートを使用して、関数のコードをいじることなくハイジャックできます。DLLによってエクスポートされていないが、そのアドレスを取得できる場合(つまり、関数のアドレスを取得することによって)、無料の(そして強く推奨される)N-CodeHookプロジェクトのようなものを使用できます。
一部の環境では、関数の最初の命令に「パッチ」を適用して、呼び出しを別の場所に移動させることができます。これは珍しい手法であり、通常のプログラミングでは使用されません。既存のコンパイル済みプログラムがあり、オペレーティング システムとの対話方法を変更する必要がある場合に使用されることがあります。
Microsoft Detoursは、これを実行できるライブラリの例です。
関数ポインタが指すものを変更することはできますが、通常の関数を変更したり、関数に含まれるものを変更したりすることはできません。
通常、関数が終了する場所を見つけることはできません。言語にはそのような標準機能はなく、コンパイラーは、関数のコードが連続しておらず、実際には単一の終点を持たないようにコードを最適化できます。コードが終了する場所を見つけるには、いくつかを使用する必要があります。非標準ツールを使用したり、コードを逆アセンブルして意味を理解したりしますが、これは自動的に実行するプログラムを簡単に作成できるものではありません。