3

実行時にCで関数を生成したいのですが、これは基本的に、メモリを割り当て、それをポイントし、関数ポインタを介して実行したいということです。これは非常に複雑なトピックであり、私の質問は単純です。また、これを実行する非常に堅牢なライブラリがいくつかあることも認識しています(例:nanojit)。

でも、基本から始めてテクニックを学びたいと思います。知識のある人がCで非常に簡単な例を教えてくれませんか?

編集: 以下の答えは素晴らしいですが、これはWindowsの同じ例です:

#include <Windows.h>

#define MEMSIZE 100*1024*1024
typedef void (*func_t)(void);

int main() {

    HANDLE proc = GetCurrentProcess();
    LPVOID p = VirtualAlloc(
        NULL,
        MEMSIZE,
        MEM_RESERVE|MEM_COMMIT,
        PAGE_EXECUTE_READWRITE);

    func_t func = (func_t)p;
    PDWORD code = (PDWORD)p;
    code[0] = 0xC3; // ret

    if(FlushInstructionCache(
        proc,
        NULL,
        0))
    {
        func();
    }

    CloseHandle(proc);
    VirtualFree(p, 0, MEM_RELEASE);
    return 0;
}
4

3 に答える 3

4

他のポスターが以前に言ったように、あなたはあなたのプラットフォームをかなりよく知る必要があるでしょう。

オブジェクトポインタを関数ポインタにキャストする問題を無視して、技術的にはUBです。これは、x86 / x64 OS X(および場合によってはLinuxでも)で機能する例です。生成されたコードは、呼び出し元に戻るだけです。

#include <unistd.h>
#include <sys/mman.h>

typedef void (*func_t)(void);

int main() {
    /*
     * Get a RWX bit of memory.
     * We can't just use malloc because the memory it returns might not
     * be executable.
     */
    unsigned char *code = mmap(NULL, getpagesize(),
            PROT_READ|PROT_EXEC|PROT_WRITE,
            MAP_SHARED|MAP_ANON, 0, 0);

    /* Technically undefined behaviour */
    func_t func = (func_t) code;

    code[0] = 0xC3; /* x86 'ret' instruction */

    func();

    return 0;
}

明らかに、これはプラットフォームによって異なりますが、必要な基本事項の概要を示しています。メモリの実行可能セクションの取得、命令の書き込み、命令の実行です。

于 2013-02-12T06:13:23.637 に答える
3

これには、プラットフォームを知っている必要があります。たとえば、プラットフォームでのC呼び出し規約は何ですか?パラメータはどこに保存されますか?どのレジスタが戻り値を保持しますか?どのレジスタを保存および復元する必要がありますか?それがわかったら、基本的に、コードをメモリのブロックにアセンブルするCコードを記述し、そのメモリを関数ポインタにキャストできます(ただし、これはANSI Cでは技術的に禁止されており、プラットフォームが一部のページをマークするかどうかによっては機能しません)実行不可能な別名NXビットとしてのメモリの)。

これを実行する簡単な方法は、コードを記述してコンパイルし、それを逆アセンブルして、どのバイトがどの命令に対応するかを確認することです。割り当てられたメモリをそのバイトのコレクションで埋め、それを適切なタイプの関数ポインタにキャストして実行するCコードを記述できます。

アーキテクチャとコンパイラの呼び出し規約を読むことから始めるのがおそらく最善です。次に、Cから呼び出すことができる(つまり、呼び出し規約に従う)アセンブリを作成する方法を学びます。

于 2013-02-12T05:37:25.730 に答える
2

あなたがツールを持っているなら、それらはあなたがいくつかのことをより簡単にするのを助けることができます。たとえば、適切な関数プロローグ/エピローグを設計する代わりに、これをCでコーディングできます。

int  foo(void* Data)
    {
    return (Data != 0);
    }

次に(WindowsではMicrosoftC)それを「cl / Fa/cfoo.c」にフィードします。次に、「foo.asm」を見ることができます。

_Data$ = 8
; Line 2
        push    ebp
        mov     ebp, esp
; Line 3
        xor     eax, eax
        cmp     DWORD PTR _Data$[ebp], 0
        setne   al
; Line 4
        pop     ebp
        ret     0

「dumpbin/allfoo.obj」を使用して、関数の正確なバイトが次のとおりであることを確認することもできます。

00000000: 55 8B EC 33 C0 83 7D 08 00 0F 95 C0 5D C3

バイトを正確に取得する時間を節約できます...

于 2013-02-12T07:02:32.200 に答える