dポイントプレゼントキャンペーン実施中!

sprintfを用いたフォーマット文字列攻撃に関する質問です。

main文の中で
---------------------
int main(void){
char str[50];
sprintf(str,"%s");
puts(str);
}
---------------------
を実行すると(null)で帰ってきます。
しかし関数呼び出しを行うと結果が変わってきます。
---------------------
void f(){
char str[50];
sprintf(str,"%s");
puts(str);
}

int main(void){
f();
}
---------------------
これを実行すると" ・L "のような文字化けしたものに変わりました。
sprintf(str,"%s%s");と記述することによって" ・L (null) "と、本来呼び出される箇所のものが格納されています。

なぜ関数呼び出しにすると変なところを参照してしまうのか(第3引数があると勘違いしてしまうのか)教えてください。

また、これによりどのような悪影響があるのか教えてください。
自分はプログラマが意図しない箇所を参照するため攻撃者のプログラムアドレスを格納してしまう恐れがあると考えています。

A 回答 (6件)

>なぜ関数呼び出しにすると変なところを参照してしまうのか(第3引数があると勘違いしてしまうのか)教えてください。



勘違いなどしていません。
sprintfのフォーマット指定"%s"で第3引数があり、それは文字列へのポインタと書いてあります。
同様にsprintfのフォーマット指定"%s%s"は第3引数,第4引数があり、それは文字列へのポインタということになります。

C,C++で可変長引数はどのようにして実装するのか調べてみてください。

>自分はプログラマが意図しない箇所を参照するため攻撃者のプログラムアドレスを格納してしまう恐れがあると考えています。

フォーマットをユーザーに入力させたりするなら、その可能性はあります。
ですが通常そのようなケースはないのではないでしょうか。

この回答への補足

回答ありがとうございます。

char str[50];
sprintf(str,"%s");
puts(str);

上記のプログラムをmain文内で実行したところ、第3引数の%sは(null)になったのに、関数に記述すると第3引数に文字化けしたものが入るのか理由がわかりません。
同じ処理の流れだと思うのですが、なぜ%sに格納される内容が変わるのか教えていただけないでしょうか。

>フォーマットをユーザーに入力させたりするなら、その可能性はあります。
>ですが通常そのようなケースはないのではないでしょうか。
確かにそうですね。
これは現実的ではないですよね。
ありがとうございます。

補足日時:2012/05/07 06:48
    • good
    • 0

関数呼出しだから内容が変わったわけではありません。


strには何も書き込まれていないので、
コンパイルされた時点でchar str[50]で確保されたメモリに存在したデータが見えているだけしょう。

sprintf( str, "%s" );では、strに何も書き込まれていません。
sprintf( str, "%s", "ABC" ); とか
sprintf( str, "%s%s", "ABC", "XYZ");

とすることでstrにABCやABCXYZの文字列が書き込まれます。
    • good
    • 0

>同じ処理の流れだと思うのですが、なぜ%sに格納される内容が変わるのか教えていただけないでしょうか。



未定義動作だしたまたまそうなっただけです。
前にも書きましたけど可変長引数の関数をどうやってつくるのか調べてみてください。
    • good
    • 0

この質問は、残念ながら”コンパイラの実装依存”であるため、誰も回答できません。



まず、int sprintf(char *str, const char *format, ...) は、
format に渡されたフォーマット文字列から、可変個の引数を読みに行こうとします。

ですので、
sprintf(str, "%s"); と書けば引数を2つしか渡さなくても3番目の引数の値を読みに行き、
sprintf(str, "%s%s"); なら3番目と4番目の引数を読みに行くわけです。

(質問に”第3引数があると勘違いしてしまうのか”とありますが、そうではなく、
sprintf(str, "%s") は、必ず第3引数を読みにいくと考えてください。)

この渡されなかった”3番目の引数がどのように読みだされるのか?”
について、一般的なことは、誰にも回答できません。

大抵は、コンパイラや sprintf(str, "%s"); の前にあるコード群と
そのコードの実行結果によって、様々に変わってしまうと思います。

ですので、質問にあるどちらの結果も”たまたまそうなった”に過ぎず、

・少しコードを変えたら結果も変わった。
・コンパイルオプションを変更したら結果が変わった。
・他のコンパイラでコンパイルしたら結果が変わった。

だとしても、全く不思議ではないです。

もし、あなたがアセンブラを読めるなら、どのように引数が渡され、sprintf 側で
どのように読みこまれたか調査することは可能でしょう。
でも、それもあなたが使用したコンパイラがたまたまそのように実装していたという
話に過ぎません。
    • good
    • 0

ま~未定義動作だからねぇ....

    • good
    • 0

#1にもありますが「可変長引数 C言語 実装」で検索するとか、上級者向けの参考書を読むとかして、どんなカラクリになっているか、調べてください。



結果が違うのは「たまたま」です。
例えば、次のものは、また違った結果になるのではないでしょうか。
#include <stdio.h>
int main(void){
char *a= "A";
char str[50];
char *b= "B";
sprintf(str,"%s");
puts(str);
return 0;
}

また、どのコンパイラを使っているかわかりませんが、Release/Debugビルド、最適化オプション等でも変わる可能性があります
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!