主な機能に関するいくつかの詳細

Linuxのプロセスの主な機能のスタックの内容に興味を持つようになりました。 いくつかの研究を費やし、今私はあなたに結果を提示します。

主な機能を説明するオプション:
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ヌル無効*80x00 '
2。ファイル名[0]チャー1+「/Tmp/a.out」
チャー10x00
...
env [1] [0]チャー10x00
...
チャー10x00
3。0x7fffffffe5e0env [0] [0]チャー1..
チャー10x00
...
argv [1] [0]チャー10x00
...
チャー10x00
4。0x7fffffffe5beargv [0] [0]チャー1+「/Tmp/a.out」
5。ランダムな長さの配列
6。auxvのデータ無効* []48 '
AT_NULLElf64_auxv_t16{0,0}
...
auxv [1]Elf64_auxv_t16
7。auxv [0]Elf64_auxv_t16例:{0x0e、0x3e8}
ヌル無効*80x00
...
env [1]char *8
8。0x7fffffffe308env [0]char *80x7fffffffe5e0
ヌル無効*80x00
...
argv [1]char *8
9。0x7fffffffe2f8argv [0]char *80x7fffffffe5be
10。0x7fffffffe2f0argc長整数型8 '引数の数+ 1
11。ローカル変数と引数、メインの前に呼び出される関数
12。ローカル変数メイン
13。0x7fffffffe1fcargcint4引数の数+ 1
0x7fffffffe1f0argvchar **80x7fffffffe2f8
0x7fffffffe1e8envchar **80x7fffffffe308
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

Source: https://habr.com/ru/post/J128111/


All Articles