現在、購入したデータスタックをCでコンパイルしています。バックグラウンドのgccを使用して、独自のツールを使用してコンパイルしています。必要に応じて、フラグとパラメーターをgccに渡すことができます。どのファイルからmain()が使用されているのか知りたいです。つまり、プロジェクトでは、どのファイルが開始点になります。どのファイルからmain()が取得されているのかわからない場合、ファイルのリストなどを生成するようにgccに指示する方法はありますか?ありがとうございました。
4 に答える
最終的な実行可能ファイルを逆アセンブルして、開始点を見つけることができます。さらに役立つ追加情報は提供していませんが。サンプル コードを使用してプロセスを示します。
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
これで、オブジェクトmain.o
は次のようになります
[root@s1 sf]# gcc -c main.c
[root@s1 sf]# nm main.o
0000000000000000 T main
U puts
メインが初期化されていないことがわかります。リンク段階で変化するからです。リンク後:
$gcc main.o
$nm a.out
U __libc_start_main@@GLIBC_2.2.5
0000000000600874 A _edata
0000000000600888 A _end
00000000004005b8 T _fini
0000000000400390 T _init
00000000004003e0 T _start
000000000040040c t call_gmon_start
0000000000600878 b completed.6347
0000000000600870 W data_start
0000000000600880 b dtor_idx.6349
00000000004004a0 t frame_dummy
00000000004004c4 T main
main にアドレスがあることがわかります。しかし、それはまだ最終的なものではありません。このメインは C ランタイムによって動的に呼び出されるためです。誰が次の役を演じるかを見ることができますU __libc_start_main@@GLIBC_2.2.5
:
[root@s1 sf]# ldd a.out
linux-vdso.so.1 => (0x00007fff61de1000) /* the linux system call interface */
libc.so.6 => /lib64/libc.so.6 (0x0000003c96000000) /* libc runime , this will invoke your main*/
/lib64/ld-linux-x86-64.so.2 (0x0000003c95c00000) /* dynamic loader */
これで、逆アセンブリを表示することでこれを確認できます。
00000000004003e0 <_start>:
..........
4003fd: 48 c7 c7 c4 04 40 00 mov rdi,0x4004c4 /* address of start of main */
400404: e8 bf ff ff ff call 4003c8 <__libc_start_main@plt> /* this will set up the environment for main, like pushing argc and argv to stack */
...........
ソースを持っていない場合は、実行可能ファイルで or への参照を検索して、実行可能libc_start_main
ファイルmain
がstart
どのように初期化され、main
.
リンクがデフォルトのリンカースクリプトで行われると、これらすべてが行われるようになりました。多くの大きなプロジェクトでは、独自のリンカー スクリプトが使用されます。プロジェクトにカスタム リンカー スクリプトがある場合、開始点の検索は、使用するリンカー スクリプトによって異なります。glibc's
ランタイムを使用しないプロジェクトがあります。その場合でも、オブジェクト ファイルやライブラリ アーカイブなどをハッキングすることで、開始点を見つけることができます。
バイナリがstripped
シンボルからのものである場合は、アセンブラーのスキルに実際に頼って、それがどこから始まるかを見つける必要があります。
私はあなたがソースを持っていないと仮定しました。つまり、スタックはいくつかのライブラリといくつかのヘッダー定義のみで配布されています (商用ソフトウェア ベンダーの一般的な慣行)。
しかし、あなたがソースを持っているなら、それはあまりにも些細なことです. grep
それを通り抜けるだけです。いくつかの回答はすでにそれを指摘しています。
どこからmain()
呼び出されるかは実装に依存します。GCC を使用すると、/usr/lib 内のスタブ オブジェクト ファイルが呼び出されるか、crt0.o
またはcrt1.o
そこから呼び出される可能性が高くなります。(このファイルには、アプリがメモリにロードされたときにカーネルによって自動的に呼び出される OS 依存のシンボルが含まれています。Linux および Mac OS X では、これは と呼ばれstart
ます)。
objdump -t
オブジェクトファイルからシンボルを一覧表示するために使用できます。したがって、Linuxを使用していると仮定し、オブジェクトファイルがまだどこかにあると仮定すると、次のことができます。
find -name '*.o' -print0 \
| xargs -0 objdump -t \
| awk '/\.o:/{f=$1} /\.text\.main/{print f, $6}'
これにより、オブジェクトファイルとmain
それに含まれる参照のリストが出力されます。通常、オブジェクトファイルからソースファイルへの単純なマップが必要です。そのシンボルを含む複数のオブジェクトファイルがある場合、実行可能バイナリごとに1つしか存在できないため、それらのどれが実際に見ているバイナリにリンクされているかによって異なりますmain
(おそらくいくつかの本当にエキゾチックな黒魔術を除く) )。
アプリケーションがリンクされ、デバッグシンボルが削除された後は、通常、特定の機能がどのソースファイルから取得されたかは示されません。これの例外は、たとえば__FILE__
マクロを使用して、文字列リテラルとして関数名を含むファイルです。デバッグシンボルを削除する前に、デバッガーを使用してその情報を取得できます。デバッグシンボルが含まれている場合、つまり。
デバッグシンボルを使用してプロジェクトをコンパイルgdb
し、実行可能ファイルから開始して、を書き込みlist main
、その後に' break
'または直接を記述しbreak main
ます。