C言語から機械語を直接実行する方法
v8(JavaScript Engine)を読んでいると、機械語を直接生成してから、それを関数型に流し込んで実行していることが分かってきました。面白いと思ったのでC言語で簡単に再現してみました。
コード
#include <stdio.h> #include <sys/mman.h> #include <string.h> #include <assert.h> int sum(int a, int b) { return a + b;} int main(void) { unsigned char* buf; int prot = PROT_READ | PROT_WRITE | PROT_EXEC; buf = mmap(NULL, 80, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memset(buf, 0, 80); int i = 0; buf[i++] = 0x55; // push %ebp buf[i++] = 0x89; buf[i++] = 0xe5; // mov %esp, %ebp buf[i++] = 0x8b; buf[i++] = 0x45; buf[i++] = 0x0c; // mov 0xc(%ebp), %eax buf[i++] = 0x8b; buf[i++] = 0x55; buf[i++] = 0x08; // mov 0x8(%ebp), %edx buf[i++] = 0x8d; buf[i++] = 0x04; buf[i++] = 0x02; // lea (%edx,%eax,1),%eax buf[i++] = 0x5d; // pop %ebp buf[i++] = 0xc3; // ret assert(!memcmp(buf, sum, i));// 関数sumとbufが等しいことを確認 int (*func)(int , int); func = (void*)buf; assert(3 == (*func)(1, 2) && "1+2=3"); assert(100 == (*func)(99, 1) && "99+1=100"); assert(8 == (*func)(3, 5) && "3+5=8"); assert(5 == (*func)(3, 2) && "3+2=5"); printf("all tests passed.\n"); munmap(buf, 80); return 0; }
機械コードの調べ方
機械コードはcpuに依存していますが、ぼくはgccとobjdumpに教えてもらうことにしました。まず次のコードをンパイル(gcc
#include <stdio.h> #include <sys/mman.h> #include <string.h> #include <assert.h> int sum(int a, int b) { return a + b;} int main(void) { unsigned char* buf; int prot = PROT_READ | PROT_WRITE | PROT_EXEC; buf = mmap(NULL, 80, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memset(buf, 0, 80); memcpy(buf, sum, 80); int (*func)(int, int); func = (void*)buf; assert(3 == (*func)(1,2) && "1 + 2 = 3"); int i = 0; while(i < 80) printf("%x\n", buf[i++]); munmap(buf, 80); return 0; }
結果として"55\n89\ne5\n8b\n45\nc\n8b\n55\n8\n8d..."が出力されます。このコードは関数sumのエントリーポイントからのコードを16進数で表したものです。sumの最後のところを調べるためにobjdump -d a.out して、sumがc3のところで終わることを確認します。これで関数sumの機械コードが分かりました。(objdumpの出力の意味が分かる場合はobjdump -d a.outではじめから関数sumのコードを調べれば良いです)