2016-12-02 9 views
3

私はWin32で実行できるバッファを割り当てたいのですが、Visual Studioでmalloc関数が実行不可能なメモリゾーンを返すという例外があります。私は無効にするNXのフラグを読んで...私の目標は、性能を心に留めて、飛行中にバイトコードをasm x86に変換することです。実行可能メモリバッファを割り当てる方法は?

somemoneは私を助けることができますか?

JS

+1

あなたはおそらく[asmJit](https://github.com/asmjit/asmjit)を見てみることをお勧めします。これはJITを書く際のアセンブリ/コードを実行できるようにメモリ保護ビットを設定します。 – BeeOnRope

答えて

7

あなたはそのためにmallocを使用しません。とにかく、C++プログラムではなぜでしょうか?ただし、実行可能メモリにはnewを使用しないでください。メモリを予約するWindows固有のVirtualAlloc関数があります。この関数は、PAGE_EXECUTE_READフラグを適用してVirtualProtect関数を実行可能としてマークします。

これを実行したら、割り当てられたメモリへのポインタを適切な関数ポインタ型にキャストし、関数を呼び出すことができます。完了したらVirtualFreeに電話することを忘れないでください。ここで

は++だけで、これは現代のCで行うことができる方法をお見せするために、(プログラムプリント5)なしエラー処理や他の健全性チェックといくつかの非常に基本的なサンプルコードです:

#include <windows.h> 
#include <vector> 
#include <iostream> 
#include <cstring> 

int main() 
{ 
    std::vector<unsigned char> const code = 
    { 
     0xb8,     // move the following value to EAX: 
     0x05, 0x00, 0x00, 0x00, // 5 
     0xc3     // return what's currently in EAX 
    };  

    SYSTEM_INFO system_info; 
    GetSystemInfo(&system_info); 
    auto const page_size = system_info.dwPageSize; 

    // prepare the memory in which the machine code will be put (it's not executable yet): 
    auto const buffer = VirtualAlloc(nullptr, page_size, MEM_COMMIT, PAGE_READWRITE); 

    // copy the machine code into that memory: 
    std::memcpy(buffer, code.data(), code.size()); 

    // mark the memory as executable: 
    DWORD dummy; 
    VirtualProtect(buffer, code.size(), PAGE_EXECUTE_READ, &dummy); 

    // interpret the beginning of the (now) executable memory as the entry 
    // point of a function taking no arguments and returning a 4-byte int: 
    auto const function_ptr = reinterpret_cast<std::int32_t(*)()>(buffer); 

    // call the function and store the result in a local std::int32_t object: 
    auto const result = function_ptr(); 

    // free the executable memory: 
    VirtualFree(buffer, 0, MEM_RELEASE); 

    // use your std::int32_t: 
    std::cout << result << "\n"; 
} 

それは比較して、非常に珍しいです通常のC++メモリ管理にはなりますが、本当にロケット科学ではありません。難しい部分は、実際のマシンコードを正しく取得することです。ここの例は非常に基本的なx64コードです。

+1

C++プログラムでmallocを使用する理由は、ヒープから動的メモリを割り当てることです。完璧に正常で正常です。 –

+0

@KyleSweet:C++では、フリーストア(ヒープではない)から動的に割り当てるために、 'new'(' 'normal''または' 'std :: allocator''のような新しい配置)を使用します。あなたは技術的に 'malloc'を使うことができますが、あなたは効果的にC言語を書いていて、(慣用的な)C++を書いていません。 OPの質問については、 'VirtualAlloc'と' malloc'の区別も重要です。 –

+1

私はちょうどあなたが提起した質問に答えていた。あなたが望むのはヒープからの素早く汚れたメモリなので、mallocを使うのを恐れないでください! –

4

割り当てられるページの領域についてVirtualAlloc

flProtect

[IN]メモリ保護のためdocumentationに記載されているとおり。ページがコミットされている場合は、メモリ保護定数のいずれかを指定できます。それらの

いずれかになります。

PAGE_EXECUTE 0x10を は、ページのコミット地域へのアクセスを実行できます。コミットされた領域に書き込もうとすると、アクセス違反が発生します。 このフラグは、CreateFileMapping関数ではサポートされていません。

PAGE_EXECUTE_READ 0x20 コミットされたページ領域に対して実行アクセスまたは読み取り専用アクセスを有効にします。コミットされた領域に書き込もうとすると、アクセス違反が発生します。 Windows Server 2003およびWindows XP:この属性は、Windows XP SP2およびWindows Server 2003 SP1以降では、CreateFileMapping関数でサポートされていません。

PAGE_EXECUTE_READWRITEx40 コミット済みページ領域に対する実行、読み取り専用、または読み取り/書き込みアクセスを有効にします。 Windows Server 2003およびWindows XP:この属性は、Windows XP SP2およびWindows Server 2003 SP1以降では、CreateFileMapping関数でサポートされていません。

などhere

+0

これはメモリの* pages *に関係するようです。これはここに適切ですか? –

+0

はい。モードを制御する必要がある場合は、_pages_を割り当てる必要があります。 – bmargulies

-2

からコンパイル時に、リンカは、データセクションとコードセクションにメモリを割り当てることによって、あなたのプログラムのメモリフットプリントを整理します。 CPUは、プログラムカウンタ(ハードCPUレジスタ)の値がコードセクション内に残っていることを確認するか、メモリ境界に違反してCPUがハードウェア例外をスローします。これは、プログラムが有効なコードのみを実行するようにして、セキュリティを提供します。 Mallocはデータメモリの割り当てを意図しています。アプリケーションにはヒープがあり、ヒープのサイズはリンカーによって設定され、データメモリとしてマークされます。したがって、実行時にmallocはヒープから仮想メモリの一部を取得するだけです。これは常にデータになります。

私はこのことが何が起こっているかをより深く理解するのに役立ちますが、あなたが必要とされる場所にあなたを得るには不十分かもしれません。おそらく、ランタイム生成コードの "コードヒープ"またはメモリプールを事前に割り当てることができます。あなたはおそらくこれを達成するためにリンカを騒がす必要がありますが、私は詳細を知りません。上記の答えを拡張

+0

*動的に割り当てられたメモリのページに対する読み取り/書き込み/実行の権限は、リンカとは関係ありません。 *静的に*書き込み+実行ページを割り当てたい場合は、配列上のリンカースクリプトやGNU C __attribute__のものを使用することができます。 –

+1

また、プログラムカウンタ(x86-64上のRIP)が特定の範囲内にとどまっていることを確認することでメモリ保護が機能しません。これがセグメンテーションがどのように機能するか(ベース/リミットあり)ですが、x86-64は64ビットモードでセグメント制限をサポートしていません。メモリ保護はページ単位で行われ、ページテーブルのビットがOSによって設定されます。図については、https://stackoverflow.com/questions/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-を参照してください。 –

3

、良い習慣がある:

  • VirtualAllocでメモリを割り当て、読み書きアクセスを。
  • するために実行する - 読み取りアクセスVirtualProtectと地域の保護というあなたのコード
  • 変更してその領域を埋めるジャ​​ンプだから、このようになります

この地域のエントリポイントを呼び出す/へ:

adr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 
// write code to the region 
ok = VirtualProtect(adr, size, PAGE_EXECUTE_READ, &oldProtection); 
// execute the code in the region 
+1

specによると、最後のパラメータがNULLの場合、VirtualProtectは失敗します。 (ダミーの)出力変数へのポインタの使用は必須です。 – user3042599

+0

@ user3042599:Thx。修正しました。 – zx485

関連する問題