読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

実行時The World!

 今日はサンクと継続の勉強会に言ってきたわけですが、そこで実行時にマシンコードを生成して呼び出すという話が出てきたので昔ちょっとやって諦めた実行時に"The World!"と表示するプログラムを生成して呼び出すに再チャレンジしてみました。
 というのも昔は普通にmallocで確保した領域に書き込んでいたので、メモリ保護の話を聞いたときこれだ!と思ったわけですね。
 で、成功したコードがこれです。

#include <stdio.h>
#include <Windows.h>

int main(void) {
    unsigned char *func = VirtualAlloc(NULL, sizeof(char) * 32, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (!func) {
        return -1;
    }
    *func = 0x55;
    *(unsigned short *)(func + 0x01) = 0xEC8B;
    *(func + 0x03) = 0x68;
    *(unsigned long *)(func + 0x04) = (unsigned long)"The World!\n";
    *(func + 0x08) = 0xE8;
    *(unsigned long *)(func + 0x09) = (unsigned long)&printf - (unsigned long)(func + 0x0d);
    *(func + 0x0d) = 0x59;
    *(func + 0x0e) = 0x5d;
    *(func + 0x0f) = 0xc3;
    ((void (*)(void))func)();
    VirtualFree(func, 0, MEM_RELEASE);
    return 0;
}

 邪悪なCスタイルキャストが頻発していますが、これはC言語なのでいいのです。で、VirtualAllocで確保した領域に実行読み書き権限を与えるところがミソのようです。
 因みに前回は、

void test(void) {
  printf("The World!\n");
}

BCCでexeを吐いてそれをOllydbgでみながらマシンコードを手で打っていくということをやっていた(と思う)のですが、その時のコードのmallocをVirtualAllocに置き換えたらすぐに行けると思ったもののそんなことは全然なくて、マシン語が書き込まれてなかったりそもそも間違ってたり、printf呼び出しアドレスの計算が間違ってたりと散々で、しかも落ちていた最後の原因はエンディアンの問題に見事に引っかかって、Ollydbgで2番目のEC8Bが8BECと表示されるのをそのまま書き込んでて落ちていたというなんとも知ってた系ミスの塊でした。
 試しに上記のコードのViarualAlloc系をmalloc系に置き換えてBCCコンパイルしたら普通に実行できたわけです。そしてGCCmalloc系でそのまま実行できました。因みにVC++2010でコンパイルして実行すると例外吐いてこけます。
 なんと、メモリ保護をやってくれるのは実はVC++コンパイルした場合のみらしいということがここで明るみに出たわけでした。