アプリ版:「スタンプのみでお礼する」機能のリリースについて

現在C言語で,関数ポインタ型による関数の呼び出しを勉強しています.
print()という関数は受け取った文字列を表示するだけの関数ですが,
これを通常の関数ポインタを用いて実行したのがmain内の(*mem)("FROM POINTER.");です.
これに対して後半は,mallocで確保した領域にmemcpyでprint()をコピーし,
(*mem)("FROM COPIED AREA.");で実行しています.

実行環境は以下の通りです.
OS:Windows7 Personal 32bit
CPU:Intel Core i5 M430
統合開発環境:Visual C++ 2010 Express Edition

コンパイルには成功しています.
実行すると"FROM POINTER."は表示されるのですが,
"FROM COPIED AREA."は表示されず,プログラムが動作を停止してしまいます.
mem = (void (*)(char*))tmp;までは実行できているようですが,
(*mem)("FROM COPIED AREA.");の実行の時点で停止しているようです.

またコマンドプロンプトからBorland C++ 5.5.1でコンパイルしても,
同じように動作が停止してしまいます.
ただし同じプログラムでも,Cygwinからgccでコンパイルすると,
意図した通りの挙動となっていることを確認しています.
コンパイラの違いによるものなのか,
UNIXとWindowsの違いによるものなのか判断できずにいます.
(もしくはライブラリのmalloc,memcpy辺りの実装法の違いでしょうか?)

どなたかご存知でしょうか.

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

#define SIZE 512

void print(char* str){
printf("%s\n", str);
}

int main(int argc, char** argv){
void (*mem)(char*);
void *tmp;

mem = &print;
(*mem)("FROM POINTER.");

tmp = malloc(SIZE);
memcpy(tmp, print, SIZE);
mem = (void (*)(char*))tmp;
(*mem)("FROM COPIED AREA.");
}

A 回答 (5件)

>セキュリティが堅めだと言われているUNIX系ではこのような書き方が問題ないのに,Windowsでは禁止されているというのは少し不思議です.近い将来,UNIX系でも機械語の挿入によるウイルス実行が流行れば似たような保護機能を付けるかもしれませんね.



もしかしてcygwinでそうだったからUNIX系はそのようになってると思ってらっしゃいますか?
cygwinはライブラリレベルでUNIXをエミュレートしてるだけでUNIXではありませんが。
たしかエミュレートするために色々小細工してたと思いますよ。

UNIX系はmallocの実装にもよりますけどmmapを利用しているのなら、その領域は実行不可に設定されてると思いますし。
    • good
    • 0
この回答へのお礼

>cygwinはライブラリレベルでUNIXをエミュレートしてるだけでUNIXではありません
細部は色々と違うということですか.
No.4の方もテストして下さっていますが,ディストリビューションごとにも違うようです.
UNIXだから,という発想からいったん離れて確認してみます.

ありがとうございました.

お礼日時:2012/07/22 21:42

Linux(Scientific linux 6)とOSX(10.7)のgccでコンパイルしてみましたが、コンパイラはwarningも出ませんね。

正常に終了しました。
実行させると両方ともエラーで落ちます。gdbで確認しましたところ
OSXは(*mem)("FROM COPIED AREA.");
でheap領域に飛ぼうとしたときにbus errotで
Linuxはmemcpy(tmp, print, SIZE);
で落ちます。こちらはなぜ落ちるかはわかりませんが、両方とも実行はできませんでした。

むしろCygwinのメモリ管理の方が問題では。
    • good
    • 0
この回答へのお礼

OSごとに落ちる部分が違って面白いですね.
許可している/禁止しているの違いなのか,
OSが想定している/していないの違いなのか,気になりますね.
Cygwinではなぜ可能なのかということも調べてみます.

お礼日時:2012/07/22 21:54

最低限コンパイラが生成したコードがリロケータブルでないと動きませんよ。


他にもヒープ領域で実行できるようになってるかなどもありますけど。
    • good
    • 0

少なくとも、print関数のコンパイル後のサイズがSIZEバイト以上あったら、全部をコピーできてませんよね。

    • good
    • 0
この回答へのお礼

SIZEが小さいと,Cygwinでも動かなくなるのは確認しました.けれど,たとえば50000くらいにしてもWindowsでは実行できないので,別の問題かと思います.
回答ありがとうございました.

お礼日時:2012/07/22 01:03

ライブラリの違い…でしょうね。



ちなみに、セキュリティを考慮するとヒープ領域に置いたコードを実行するのは好ましくありません。
# DEP(データ実行防止)の設定によっては動作させようとするとOSに殺されます。
# Windows7なら…[システムのプロパティ]->「詳細設定」タブ->パフォーマンスの[設定]ボタン->「データ実行防止」タブで設定。

また、コード領域にあるものをヒープ領域にただコピーしただけで問題がないか…はライブラリによるでしょう。
今回は別のライブラリ(printf())をコールするだけでしたから、環境によっては問題にならないかも知れませんが、
コピーしたマシン語でアドレスが異なってしまう場合などに吹っ飛びかねません。
# 実行ファイルやDLLはメモリに読み込まれた時点でアドレス再配置が行われますので大丈夫ですが、メモリ上に配置が終わったものの場合は再配置は動作しませんし。
    • good
    • 0
この回答へのお礼

ハードウェアでもDEPはサポートされており,例外を除いてDEPが有効になっていました.コンパイルしたexeファイルを例外に追加しようとしたところ,このプログラムはDEPを有効にして実行する必要があります,と言われてしまいました.そのため重要なプログラムとサービスのみに有効にする設定にして,再起動してから実行してみました.結果は変わらなかったので,DEPが作動して停止したがコマンドプロンプトにその情報が伝わっていないか,OSが動く前にライブラリが原因で動作が停止したのかと思います.
セキュリティが堅めだと言われているUNIX系ではこのような書き方が問題ないのに,Windowsでは禁止されているというのは少し不思議です.近い将来,UNIX系でも機械語の挿入によるウイルス実行が流行れば似たような保護機能を付けるかもしれませんね.

確かにプログラムの配置位置と参照するメモリの位置の関係も,考慮しないといけませんね.本来データ用に後から確保された領域を,命令用に使用するといった手法を用いるウイルスがどうやってこの問題を解決しているのか少し興味がわきました.(私は決して作りません!)

詳しくありがとうございました.

お礼日時:2012/07/22 01:01

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