主な機能を説明するオプション:
1. int main()
2. int main(int argc、char ** argv)
3. int main(int argc、char ** argv、char ** env)
4. int main(int argc、char ** argv、char ** env、ElfW(auxv_t)auxv [])
5. int main(int argc、char ** argv、char ** env、char ** apple)
argc-パラメーターの数
argv-コマンドラインパラメータ文字列へのポインタのヌル端末配列
envは、環境変数の文字列へのポインターのヌル終端配列です。 NAME = VALUE形式の各行
auxv-補助値の配列(PowerPC [1]でのみ使用可能)
apple-実行可能ファイルへのパス(MacOSおよびDarwin [2]上)
補助ベクトルは、有効なユーザー識別子、setuidビット属性、メモリページサイズなど、さまざまな追加情報を持つ配列です。
さらに、i386およびx86_64の補助値の配列、およびスタックの「セグメント」の残りの内容を取得する方法。
スタックセグメントのサイズは、マップファイルで確認できます。
猫/ proc / 10918 /マップ
...
7ffffffa3000-7ffffffff000 rw-p 00000000 00:00 0 [スタック]
...
ローダーは、制御をメインに転送する前に、コマンドラインパラメーター、環境変数、および補助ベクトルの配列の内容を初期化します。
初期化後、64ビットバージョンのスタックの最上部は次のようになります。
上記のシニアアドレス。
1。 | 0x7ffffffff000 | スタックセグメントのトップポイント。 訴えはセグメンテーション違反を呼ぶ | |||
0x7ffffffff0f8 | ヌル | 無効* | 8 | 0x00 ' | |
2。 | ファイル名[0] | チャー | 1+ | 「/Tmp/a.out」 | |
チャー | 1 | 0x00 | |||
... | |||||
env [1] [0] | チャー | 1 | 0x00 | ||
... | |||||
チャー | 1 | 0x00 | |||
3。 | 0x7fffffffe5e0 | env [0] [0] | チャー | 1 | .. |
チャー | 1 | 0x00 | |||
... | |||||
argv [1] [0] | チャー | 1 | 0x00 | ||
... | |||||
チャー | 1 | 0x00 | |||
4。 | 0x7fffffffe5be | argv [0] [0] | チャー | 1+ | 「/Tmp/a.out」 |
5。 | ランダムな長さの配列 | ||||
6。 | auxvのデータ | 無効* [] | 48 ' | ||
AT_NULL | Elf64_auxv_t | 16 | {0,0} | ||
... | |||||
auxv [1] | Elf64_auxv_t | 16 | |||
7。 | auxv [0] | Elf64_auxv_t | 16 | 例:{0x0e、0x3e8} | |
ヌル | 無効* | 8 | 0x00 | ||
... | |||||
env [1] | char * | 8 | |||
8。 | 0x7fffffffe308 | env [0] | char * | 8 | 0x7fffffffe5e0 |
ヌル | 無効* | 8 | 0x00 | ||
... | |||||
argv [1] | char * | 8 | |||
9。 | 0x7fffffffe2f8 | argv [0] | char * | 8 | 0x7fffffffe5be |
10。 | 0x7fffffffe2f0 | argc | 長整数型 | 8 ' | 引数の数+ 1 |
11。 | ローカル変数と引数、メインの前に呼び出される関数 | ||||
12。 | ローカル変数メイン | ||||
13。 | 0x7fffffffe1fc | argc | int | 4 | 引数の数+ 1 |
0x7fffffffe1f0 | argv | char ** | 8 | 0x7fffffffe2f8 | |
0x7fffffffe1e8 | env | char ** | 8 | 0x7fffffffe308 | |
14。 | ローカル関数変数 |
'-文書内のフィールドの説明は見つかりませんでしたが、ダンプ内に明確に表示されます。
32ビットをチェックしませんでしたが、ほとんどの場合、サイズを2つに分割するだけで十分です。
1.上部のアドレスをアドレス指定すると、Segfaultが発生します。
2.実行可能ファイルへのパスを含む文字列。
3.環境変数を含む文字列の配列
4.コマンドラインパラメータを含む文字列の配列
5.配列はランダムに長くなります。 その選択はコマンドによって無効にすることができます
sysctl -w kernel.randomize_va_space = 0
エコー0> / proc / sys / kernel / randomize_va_space
6.補助ベクトルのデータ(たとえば、文字列 "x86_64")
7.補助ベクトル。 詳細は以下をご覧ください。
8.環境変数の文字列へのポインタのゼロ終端配列
9.コマンドラインパラメータ文字列へのポインタのヌル端末配列
10.コマンドラインパラメータの数を含むマシンワード(「シニア」関数の引数の1つ。段落11を参照)
11. main(_start、__ libc_start_main ..)の前に呼び出される関数のローカル変数と引数
12. mainで宣言された変数
13.メイン関数の引数
14.ローカル関数の変数と引数。
ヘルパーベクトル
i386およびx86_64の場合、補助ベクトルの最初の要素のアドレスは取得できませんが、このベクトルの内容は他の方法で取得できます。 それらの1つは、環境変数の文字列へのポインターの配列の直後にメモリ領域を有効にすることです。
次のようになります。
#include <stdio.h> #include <elf.h> int main(int argc, char** argv, char** env){ Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); // for (auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++){ printf("addr: %p type: %lx is: 0x%lx\n", auxv, auxv->a_type, auxv->a_un.a_val); } printf("\n (void*)(*argv) - (void*)auxv= %p - %p = %ld\n (void*)(argv)-(void*)(&auxv)=%p-%p = %ld\n ", (void*)(*argv), (void*)auxv, (void*)(*argv) - (void*)auxv, (void*)(argv), (void*)(&auxv), (void*)(argv) - (void*)(&auxv)); printf("\n argc copy: %d\n",*((int *)(argv - 1))); return 0; }
Elf構造{32,64} _auxv_tは/usr/include/elf.hに記述されています。 linux-kernel / fs / binfmt_elf.cの構造充填関数
ベクトルの内容を取得する2番目の方法:
hexdump / proc / self / auxv
環境変数LD_SHOW_AUXVを設定すると、最も読みやすいビューが得られます。
LD_SHOW_AUXV = 1 ls
AT_HWCAP:bfebfbff //プロセッサ機能
AT_PAGESZ:4096 //メモリページサイズ
AT_CLKTCK:100 //リフレッシュレートの時間()
AT_PHDR:0x400040 //ヘッダー情報
AT_PHENT:56
AT_PHNUM:9
AT_BASE:0x7fd00b5bc000 //インタープリターのアドレス、つまりld.so
AT_FLAGS:0x0
AT_ENTRY:0x402490 //プログラムへのエントリポイント
AT_UID:1000 //ユーザーとグループの識別子
AT_EUID:1000 //評価および有効
AT_GID:1000
AT_EGID:1000
AT_SECURE:0 // setuidフラグが立てられるかどうか
AT_RANDOM:0x7fff30bdc809 //アドレス16のランダムバイト、
起動時に生成
AT_SYSINFO_EHDR:0x7fff30bff000 //使用されるページへのポインター
//システムコール
AT_EXECFN:/ bin / ls
AT_PLATFORM:x86_64
左側は変数の名前、右側は値です。 考えられるすべての変数名とその説明は、elf.hファイルで表示できます。 (AT_プレフィックスを持つ定数)
メインから戻る()
プロセスコンテキストが初期化された後、制御はmain()ではなく_start()関数に渡されます。
main()は__libc_start_mainからすでに呼び出しています。 この最後の関数には興味深い機能があります-main()の後に実行する必要がある関数へのポインターが渡されます。 そして、このポインタはスタックを自然に通過します。
一般に、__ libc_start_main引数の形式は、ファイルglibc-2.11 / sysdeps / ia64 / elf / start.Sに応じています
/ *
* __libc_start_mainの引数:
* out0:メイン
* out1:argc
* out2:argv
* out3:init
* out4:fini //メインの後に呼び出される関数
* out5:rtld_fini
* out6:stack_end
* /
つまり finiポインタのアドレスを取得するには、最後のローカル変数mainから2マシンワードシフトする必要があります。
発生した内容は次のとおりです(パフォーマンスはコンパイラのバージョンによって異なります)。
#include <stdio.h> void **ret; void *leave; void foo(){ void (*boo)(void); // printf("Stack rewrite!\n"); boo = (void (*)(void))leave; boo(); // fini() } int main(int argc, char *argv[], char *envp[]) { unsigned long int mark = 0xbfbfbfbfbfbfbfbf; //, ret = (void**)(&mark+2); // , , (fini) leave = *ret; // *ret = (void*)foo; // return 0; // foo() }
面白かったと思います。
がんばって。
役に立つヒントをくれたXeorに感謝します。
1. www.gelato.unsw.edu.au/IA64wiki/AuxiliaryVector
2. unixjunkie.blogspot.com/2006/02/char-apple-argument-vector.html
3. articles.manugarg.com/aboutelfauxiliaryvectors.html
4. www.phrack.org/issues.html?issue=58&id=5#article
5. unixforum.org/index.php?showtopic=94993&st=30
6. sources.redhat.com/ml/libc-alpha/2007-06/msg00108.html
7. linux-kernel / fs / binfmt_elf.c
8. /usr/include/elf.h
9. glibc-2.11 / sysdeps / ia64 / elf / libc-start.c