2

bufferOverflow に関する宿題に取り組んでいます。テスト プログラムはfread()、ファイルから 4096 バイトを読み取るために使用します。

kernel.randomize_va_space を 0 に設定し、gdb でプログラムを実行すると、fread() コマンドが何も返さないことがわかります。

kernel.randomize_va_space を 1 または 2 に設定し、gdb を使用してプログラムを再実行すると、fread がファイルを保存するバッファーに期待されるデータが表示されます。

ASLR によって fread が適切に機能しなくなるのはなぜですか?

参考までに: これは ubuntu 12.0.4 64 ビットで、プログラムは -c99 および -m32 フラグを使用して gcc にコンパイルされています。

この課題のために与えられたテスト プログラムは次のとおりです。

#include <stdio.h>

#define READSIZE 0x1000

void countLines(FILE* f){
  char buf[0x400];//should be big enough for anybody
  int lines=0;
  fread(buf,READSIZE,1,f);  

  for(int i=0;i<0x400;i++)
    if(buf[i] == '\n')
      lines++;


  printf("The number of lines in the file is %d\n",lines);
  return;
}

int main(int argc,char** argv){
  if(argc<2){
    printf("Proper usage is %s <filename>\n",argv[0]);
    exit(0);
  }
  FILE* myfile=fopen(argv[1],"r"); 
  countLines(myfile);
  return 0;
}

gdb で実行すると、次の行にブレークポイントを配置します。

  for(int i=0;i<0x400;i++)

次に、gdb で次のことを行います。

(gdb) x $esp
0xbffff280    0xbffff298

私が行った場合:

(gdb) x /12wx $esp

最初の 4 バイトは buf のアドレスで、次の 4 バイトは fread に渡された 0x1000 であり、次の 4 バイトは同じく fread に渡された 0x01 であることがわかります。

これは、 のスタック フレームではなく、 fread 関数のスタック フレームのように見えますcountLines()

$esp が、終了したばかりのスタック フレームではなく、現在のスタック フレームを指していないのはなぜですか?

アップデート

次のようにコードを変更しました。

#include <stdio.h>

#define READSIZE 0x1000

void countLines(FILE* f){
  char buf[0x400];//should be big enough for anybody
  int lines=0;
int ferr=0;
  fread(buf,READSIZE,1,f);  
ferr=ferror(f);
    if (ferr)
        printf("I/O error when reading (%d)\n",ferr);
    else if (feof(f))
        printf("End of file reached successfully\n");

  for(int i=0;i<0x400;i++)
    if(buf[i] == '\n')
      lines++;

  printf("The number of lines in the file is %d\n",lines);
  return;
}

int main(int argc,char** argv){
  if(argc<2){
    printf("Proper usage is %s <filename>\n",argv[0]);
    exit(0);
  }
  FILE* myfile=fopen(argv[1],"r"); 
  countLines(myfile);
  return 0;
}

ASLR を無効にして実行すると、次のメッセージが表示されます: I/O errorwhen readin (1)

ASLR を有効にして (値 = 1) 実行すると、EOF に到達します。

4

3 に答える 3

1

fread()size 個nitemsの要素を が指す配列に読み込みます。配列が十分な大きさであることを確認するのは、プログラマの責任です。sizeptr

その最後の部分は重要です。fread()配列の実際のサイズを知る方法はありません。それは喜んで物を読み、配列の終わりを過ぎてそれを保存します。配列の末尾を超えてアクセスすると、未定義の動作が発生します。何が起こるかについての保証はありません。特に、この場合、任意のコードが実行される可能性があります。

爆撃するときとしないときがある理由については、次のとおりです。

ASLR がないと、次のようになります。

(gdb) x $esp
0xbffff280    0xbffff298

アドレス のバッファを使用します0xbffff298。スタックの最上位は で0xbfffffffあり、バッファの開始とスタックの終了の間に 3432 バイトが残ります。バッファに読み込もうとすると、プロセスはユーザー空間に何もマップされていないため(プロセスのユーザー空間マッピングを0xc0000000確認するには、 で確認できます)、基本的なシステム コールは で失敗します。cat /proc/<pid>/mapsread()EFAULT - Bad address

ASLR では、スタック配置のランダム化があります。スタックベースには、x86 ( arch/x86/include/asm/elf.h )で ~11 ビット1のランダム性があります。

/* 1GB for 64bit, 8MB for 32bit */
#define STACK_RND_MASK (test_thread_flag(TIF_IA32) ? 0x7ff : 0x3fffff)

そして、スタックトップにはさらに 9 ビットのランダム性があります ( fs/binfmt_elf.c:create_elf_tables()から呼び出されるarch/x86/kernel/process.c:arch_align_stack( ) ):

if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
    sp -= get_random_int() % 8192;
return sp & ~0xf;

したがって、arch_align_stack()スタックの先頭 [0,8176] バイトを 16 バイト単位でオフセットします。つまり、初期スタック ポインタは、元の位置から 0、16、32、...、8176 バイト移動できます。

したがって、3432 バイト (バッファー、それらへの各種ポインター用のスペース、argcおよびargvパディング) に加えて変数 ASLR オフセットを使用すると、スタック上に 4096 バイト未満になる可能性は 8% しかありません。 ASLR をオンにして SEGV を表示できます。何度も試してみると、12 回の試行ごとに約 1 回表示されるはずです。 envpauxp


1スタック ベースは、追加のページを移動することもできます。fs/exec.c:setup_arg_pages()を参照してください。

stack_top = arch_align_stack(stack_top);
stack_top = PAGE_ALIGN(stack_top);
于 2013-10-29T16:12:59.487 に答える
0

スタック上のfread()引数については、正しいです。これらは、呼び出し元の BP 値が呼び出し先によってスタックにプッシュされる前に格納されるため、呼び出し元のスタック フレームの一部です。x86 スタック フレームの説明を確認してください。x86-64 では、呼び出し先のレジスターに渡されるため、それらは表示されません。

ASLR エラーについては、 でエラーの説明を取得しますperror()。または、glibc の debuginfo パッケージをインストールして、コードfread()自体をトレースできるようにすることをお勧めします。

于 2013-10-18T15:29:58.477 に答える