2

次の C プログラム (Ubuntu で gcc でコンパイル) を実行すると、セグメンテーション違反が発生します。

#include <stdio.h>

char f[] = "\x55\x48\x89\xe5\x48\x89\x7d\xf8\x48\x89\x75\xf0\x48\x8b\x45\xf8\x8b\x10\x48\x8b\x45\xf0\x8b\x00\x89\xd1\x29\xc1\x89\xc8\xc9\xc3";

int main()
{
    int (*func)();
    func = (int (*)()) f;

    int x=3,y=5;
    printf("%d\n",(int)(*func)(&x,&y));
    return 0;
}

文字列fには、次の関数のマシン コードが含まれます。

int f(int*a, int*b)
{
    return *a-*b;
}

参照:

f.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <f>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
   8:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
   c:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  10:   8b 10                   mov    (%rax),%edx
  12:   48 8b 45 f0             mov    -0x10(%rbp),%rax
  16:   8b 00                   mov    (%rax),%eax
  18:   89 d1                   mov    %edx,%ecx
  1a:   29 c1                   sub    %eax,%ecx
  1c:   89 c8                   mov    %ecx,%eax
  1e:   c9                      leaveq 
  1f:   c3                      retq   

これは次を使用してコンパイルされます。

gcc test.c -Wall -Werror
./a.out
Segmentation fault

期待される出力は次のとおりです-2- どうすれば動作させることができますか?

4

2 に答える 2

4

以下の提案は gcc では機能しなくなりました。現在、配列データは別の実行不可能な読み取り専用 ELF セグメントに配置されているためです。

歴史的な理由からここに残しておきます。


興味深いことに、アプリケーションchar f[] = "...";に関数として aをリンクしようとしても、リンカーは文句を言いませんでした。f()関数を呼び出そうとしていますf()。実行可能ファイルにリンクされたシンボルがありますがf、驚くべきことに、それは機能ではなく、何らかの変数です。したがって、実行に失敗します。これは、スタック実行保護メカニズムが原因である可能性があります。

これを回避するには、明らかに、プロセス メモリのテキスト セグメントに文字列を取得する必要があります。文字列を として宣言すると、これを実現できますconst char f[]

Aleph One による、楽しさと利益のためにスタックを粉砕することから:

テキスト領域はプログラムによって固定され、コード (命令) と読み取り専用データが含まれます。この領域は、実行可能ファイルのテキスト セクションに対応します。

は読み取り専用でconst char[] あるため、コンパイラはそれをコードと一緒にテキスト領域に配置します。これにより、実行防止メカニズムが回避され、マシンはその中でマシンコードを実行できるようになります。


例:

/* test.c */
#include <stdio.h>

const char f[] = "\x55\x48\x89\xe5\x48\x89\x7d\xf8\x48\x89\x75\xf0\x48\x8b\x45\xf8\x8b\x10\x48\x8b\x45\xf0\x8b\x00\x89\xd1\x29\xc1\x89\xc8\xc9\xc3";

int main()
{
    int (*func)();
    func = (int (*)()) f;

    int x=3,y=5;
    printf("%d\n",(int)(*func)(&x,&y));
    return 0;
}

収量:

$ gcc test.c -Wall && ./a.out
-2

(Fedora 16、gcc 4.6.3)

于 2012-09-16T13:20:35.150 に答える
0

あなたが正しく理解している場合、テキストスペースではなく、初期化された静的ストレージにある単純なコードを実行しようとしていますか? これが失敗する場合、次の 3 つの理由しか考えられません。コードが正しく初期化されていない (この単純なケースではありそうにない)、データ スペースが踏まれている (この単純なケースではそうではないように見える)、またはシステムが故障している、のいずれかです。セキュリティ対策としてそれを防ぎます(おそらく、あなたがやろうとしていることはかなり非典型的であり、主にバッファオーバーフローの悪用に使用されるためです)。

于 2012-09-16T12:56:28.830 に答える