メモリに保存されたマシンコードを実行する方法を理解しようとしています。
次のコードがあります:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
FILE* f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
unsigned int len = ftell(f);
fseek(f, 0, SEEK_SET);
char* bin = (char*)malloc(len);
fread(bin, 1, len, f);
fclose(f);
return ((int (*)(int, char *)) bin)(argc-1, argv[1]);
}
上記のコードは GCC では正常にコンパイルされますが、次のようにコマンドラインからプログラムを実行しようとすると、
./my_prog /bin/echo hello
プログラムがセグメント違反を起こします。最後の行をコメントアウトするとセグメント違反が起こらなくなるので、問題は最後の行にあることがわかりました。
まだ関数ポインターについて理解できていないので、正しくできているとは思えません。
問題は鋳造不良か、それとも他の何かでしょうか?
ベストアンサー1
書き込み実行権限を持つページが必要です。UNIX の場合は mmap(2) および mprotect(2) を参照してください。malloc を使用してこれを実行しないでください。
また、他の人の言うことを読んでください。ローダーを使用して実行できるのは生のマシン コードだけです。ELF ヘッダーを実行しようとすると、やはりセグメント違反が発生する可能性があります。
返信とダウンモッドの内容に関して:
1- OP はマシン コードを実行しようとしていると言ったので、実行可能ファイルを実行するのではなく、そのように返信しました。
2- malloc 関数と mman 関数を混在させない理由を確認します。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
int main()
{
char *a=malloc(10);
char *b=malloc(10);
char *c=malloc(10);
memset (a,'a',4095);
memset (b,'b',4095);
memset (c,'c',4095);
puts (a);
memset (c,0xc3,10); /* return */
/* c is not alligned to page boundary so this is NOOP.
Many implementations include a header to malloc'ed data so it's always NOOP. */
mprotect(c,10,PROT_READ|PROT_EXEC);
b[0]='H'; /* oops it is still writeable. If you provided an alligned
address it would segfault */
char *d=mmap(0,4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANON,-1,0);
memset (d,0xc3,4096);
((void(*)(void))d)();
((void(*)(void))c)(); /* oops it isn't executable */
return 0;
}
Linux x86_64 ではまさにこの動作が発生し、他の実装では必ず発生するその他の醜い動作が発生します。