7

Insecure Programmingのabo3.cの例を見ていますが、以下の例のキャストを理解していません。誰かが私を啓発できますか?

int main(int argv,char **argc)   
{  
    extern system,puts;  
    void (*fn)(char*)=(void(*)(char*))&system;  
    char buf[256];  

    fn=(void(*)(char*))&puts;  
    strcpy(buf,argc[1]);  
    fn(argc[2]);  
    exit(1);  
}

それで、システムとプットのキャスティングとは何ですか?どちらも を返すintので、なぜそれを void にキャストするのですか?

プログラム全体を視野に入れて説明していただければ幸いです。

[編集]
ご意見ありがとうございます。

Jonathan Leffler さん、実際にはコードが「悪い」のには理由があります。これは、バッファや関数ポインタのオーバーフローなどで悪用可能であると考えられています。mishou.orgには、上記のコードを悪用する方法に関するブログ投稿があります。その多くはまだ私の頭の上にあります。

bta、私は上記のブログ投稿から、キャスト システムが何らかの形でリンカによる削除を妨げていることを収集しました。

すぐにはっきりしないことの 1 つは、システムとプットのアドレスが両方とも同じ場所に書き込まれていることです。これは、gera が「リンカがそれを削除しないように」と話していることかもしれません。

関数ポインターの話題ですが、構文がより明確になったので、フォローアップの質問をしたいと思います。関数ポインターを使用したより高度な例をいくつか見ていたところ、シェルコードをホストしているサイトから取得したこの忌まわしきものに出くわしました。

#include <stdio.h>

char shellcode[] = "何らかのシェルコード";

int main(void)
{
    fprintf(stdout,"長さ: %d\n",strlen(シェルコード));
    (*(void(*)()) シェルコード)();
}

voidそれで、配列は、参照され、呼び出されて返される関数にキャストされていますか? それは見栄えが悪いだけです。では、上記のコードの目的は何でしょうか?

[/編集]

4

2 に答える 2

10

元の質問

ユーザーbtaは、キャストについて正しい説明を行い、キャストの非効率性についてコメントしましたsystem

私は追加するつもりです:

ラインはexternせいぜい奇妙です。厳密な C99 では型がなく、無効になるため誤りです。C89 では、型は と見なされますint。行には、「system と呼ばれる外部定義の整数と、puts と呼ばれる別の整数があります」と書かれていますが、これは正しくありません。これらの名前を持つ関数のペアがあります。リンカーが関数を想定された整数に関連付ける可能性があるため、コードは実際に「機能する」可能性があります。しかし、ポインターがint. もちろん、コードには正しいヘッダー ( <stdio.h>forputs()<stdlib.h>for system()exit()および<string.h>for strcpy()) が含まれている必要があります。

2 つの別々のexit(1);点で悪いです。

  • 無条件に失敗を示します。0 またはで終了し、EXIT_SUCCESS成功を示します。

  • 私の見解では、よりもreturn最後に使用する方が良いです。誰もが必ずしも私に同意するわけではありませんが、. 唯一の言い訳は、 で定義されたローカル変数の継続的な存在に依存する に登録された関数など、他の悪い慣行からの問題を回避することです。main()exit()exit()main()atexit()main()


/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c nasty.c
nasty.c: In function ‘main’:
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘system’
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘puts’
nasty.c:3: warning: built-in function ‘puts’ declared as non-function
nasty.c:8: warning: implicit declaration of function ‘strcpy’
nasty.c:8: warning: incompatible implicit declaration of built-in function ‘strcpy’
nasty.c:10: warning: implicit declaration of function ‘exit’
nasty.c:10: warning: incompatible implicit declaration of built-in function ‘exit’
nasty.c: At top level:
nasty.c:1: warning: unused parameter ‘argv’

コードが良くない!私は、そのようなコードを含み、すべての恐ろしさを説明していない情報源について心配しています (そのような乱雑なコードを表示する唯一の言い訳は、それを分析して修正することだからです)。


コードには別の奇妙な点があります。

int main(int argv,char **argc)   

それは「正しい」(動作する) ですが、100% 慣習的ではありません。通常の宣言は次のとおりです。

int main(int argc, char **argv)

名前は「引数カウント」と「引数ベクトル」の略でありargc、文字列のベクトル (配列) の名前として使用するのは異常であり、実に紛らわしいものです。


参照されているサイトにアクセスすると、一連の段階的な例が示されていることがわかります。作者が単に argc/argv の問題に盲点を持っているのか、それとも意図的にいじっているのかはわかりません ( 「abo1」は彼がプレイしていることを示唆していますが、私の見解では役に立ちません)。例はあなたの心を養うことになっていますが、それらが何をするかについての説明はあまりありません。あまりお勧めできないサイトだと思います。


拡張質問

このコードのキャストは何をしていますか?

#include <stdio.h>

char shellcode[] = "some shellcode";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

これは文字列 'shellcode' のアドレスを取得し、不確定な一連の引数を取り、値を返さず、引数なしで実行する関数へのポインタとして扱います。この文字列には、エクスプロイト (通常はシェルを実行する) 用のバイナリ アセンブラ コードが含まれており、侵入者の目的は、root 権限を持つプログラムにシェルコードを実行させ、root 権限でコマンド プロンプトを表示させることです。そこから、システムは自分のものになります。練習のための最初のステップは、もちろん、非ルート プログラムにシェルコードを実行させることです。

分析のレビュー

Mishouの Web サイトでの分析は、私が望むほど信頼できるものではありません。

1 つは、このコードは C 言語の extern キーワードを使用して、システムを作成し、関数を使用できるようにします。これが(私が思うに)何をするかというと、基本的には(暗黙の)ヘッダーファイルで定義された関数の場所を直接参照することです...私は、GDBがシステム用のヘッダーファイルstdlib.hとプット用のstdio.hを自動的に魔法のようにインクルードしているという印象を受けます. すぐにはっきりしないことの 1 つは、システムとプットのアドレスが両方とも同じ場所に書き込まれていることです。これは、gera が「リンカがそれを削除しないように」と話していることかもしれません。

解説の分析:

  1. 最初の文はあまり正確ではありません。systemシンボルとputsが別の場所で (整数として) 定義されていることをコンパイラに伝えます。コードがリンクされると、puts()関数のアドレスがわかります。コードはそれを整数変数として扱いますが、整数変数のアドレスは実際には関数のアドレスであるため、キャストによってコンパイラは最終的にそれを関数ポインタとして扱うようになります。
  2. 2 番目の文は完全には正確ではありません。リンカーは、関数シンボルsystem()puts()C ライブラリを介して、外部の「変数」のアドレスを解決します。
  3. GDB には、コンパイルまたはリンクのプロセスを行うものは何もありません。
  4. 最後の文はまったく意味がありません。初期化と同じ変数への割り当てがあるため、アドレスは同じ場所にのみ書き込まれます。

これは、記事全体を読む動機にはなりませんでした。デューデリジェンスは私を前進させます。その後の説明はより良いものですが、それでも私が思っているほど明確ではありません. しかし、長すぎるが慎重に作成された引数文字列でバッファをオーバーフローさせる操作は、操作の核心です。コードには と の両方が記載puts()system()れているため、非エクスプロイト モードで実行された場合、puts()関数は既知のシンボルになり (そうでない場合は、dlopen()そのアドレスを見つけるために を使用する必要があります)、エクスプロイト モードで実行された場合、コードにはシンボルが含まれます。system()直接使用可能。使用されていない外部参照は、実行可能ファイルでは使用できません。これは、ヘッダーを含むプログラムで使用される数と比較して、典型的なシステム ヘッダーにシンボルがいくつあるかを理解すると良いことです。

これらのトリックの実装は特定のページには示されていませんが、いくつかの巧妙なトリックが示されています。getenvaddrプログラムの情報が利用可能であると仮定します (確認はしていません) 。

abo3.c コードは次のように記述できます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)   
{  
    void (*fn)(char*) = (void(*)(char*))system;  
    char buf[256];  

    fn = (void(*)(char*))puts;  
    strcpy(buf, argv[1]);  
    fn(argv[2]);  
    exit(1);  
}

今では、私が最初に使用した面倒なコンパイル オプションで 1 つの警告だけでコンパイルされます。これは、'argc' が使用されていないという正確な警告です。オリジナルと同じように悪用可能です。ただし、きれいにコンパイルされるため、「より良い」コードです。インダイレクションは不必要な神秘的なものであり、コードを悪用可能にする重要な部分ではありませんでした。

于 2011-02-19T17:16:20.137 に答える
4

systemputs通常は両方を返しintます。voidコードは、おそらく、返される値を無視したいために、を返すポインターにそれらをキャストしています。これは(void)fn(argc[2]);、キャストがリターンタイプを変更しなかった場合に、最後から2番目の行として使用するのと同じです。リターンタイプのキャストはコールバック関数に対して行われることがあり、このコードスニペットはコールバックの単純な例のようです。

systemそれが決して使われないのになぜキャストするのかは私を超えています。ここに示されていないコードが他にもあると思います。

于 2011-02-19T16:58:27.560 に答える