2012-03-06 3 views
16

私は実行時にアセンブラコードを生成するソフトウェアを開発中です。例えば、ここで は(Win64のABI用)GetCurrentProcess関数を呼び出すためのアセンブラコードを生成し、非常に単純な機能です:実行時にコード生成を簡素化するにはどうすればよいですか?

void genGetCurrentProcess(char *codePtr, FARPROC addressForGetCurrentProcessFunction) 
{ 
#ifdef _WIN64 
    // mov rax, addressForGetCurrentProcessFunction 
    *codePtr++ = 0x48 
    *codePtr++ = 0xB8; 
    *((FARPROC *)codePtr)++ = addressForGetCurrentProcessFunction; 

    // call rax 
    *codePtr++ = 0xFF; 
    *codePtr++ = 0xD0; 
#else 
    // mov eax, addressForGetCurrentProcessfunction 
    *codePtr++ = 0xB8; 
    *((FARPROC *)codePtr)++ = addressForGetCurrentProcessFunction; 

    // call eax 
    *codePtr++ = 0xFF; 
    *codePtr++ = 0xD0; 
#endif 
} 

通常、私は、インラインアセンブラを使用していますが、残念ながらしたい - これはではないようですもはや64ビットMSVCコンパイラで可能です。私がそれをしている間、このコードはMSVC10とMinGWまでMSVC6で動作するはずです。 genGetCurrentProcessのような多くの関数がありますが、それらはすべてアセンブラコードを生成し、それらの多くは引数として渡される関数ポインタを取得します。

このコードを変更すると、エラーが発生しやすくなります。手動でABI固有の処理を行う必要があります(たとえば、レジ​​スタを消費する関数を呼び出す前に32バイトのスタック領域を確保するなど)。

私の質問は、実行時にアセンブラコードを生成するためにこのコードを単純化できますか?私の希望は、アセンブラコードを何らかの形で直接書くことができるということでした。外部ファイルにはおそらくml/ml64を使ってアセンブルされましたが、アセンブルされたコードの一部のバイトがランタイム(上記の例ではaddressForGetcurrentProcessFunctionの値)。たぶん、いくつかのコードを組み立てることは可能ですが、ランタイムにコードを簡単に変更してそれを私のバッファにコピーできるように、コード内の特定の場所に 'ラベル'を割り当てますか?

+2

これはインラインアセンブリではないため、インラインマシンコードです! –

+0

なぜこれを行う必要がありますか? GetCurrentProcess()をCコードから直接呼び出さないのはなぜですか? – user9876

+0

@ user9876:リモート(中断)プロセスでコードを修正しています。このプログラムは、Unixシステム上で 'LD_PRELOAD'と同様の影響を与えます。 –

答えて

10

asmjitをご覧ください。ランタイムコード生成のためのC++ライブラリです。 x64とおそらくほとんどの既存の拡張機能(FPU、MMX、3dNow、SSE、SSE2、SSE3、SSE4)をサポートします。そのインターフェイスはアセンブリ構文に似ており、指示が正しくエンコードされます。

+0

+1 - asmjitは面白そうに見えますが、私はそれをまだ認識していませんでした!それに言及してくれてありがとう。 –

0

いくつかのヘルパー関数とマクロを記述することで、命令エンコーディング、呼び出し規約、CPUモード関連の詳細を抽象化することができます。

数値的にコード化され、配列に含まれる擬似asmコードを実行可能コードにアセンブルする小さなアセンブラを作成することもできます。

UINT32 blah[] = 
{ 
    mov_, ebx_, dwordPtr_, edi_, plus_, eax_, times8_, plus_, const_, 0xFEDCBA98, 
    call_, dwordPtr_, ebx_, 
}; 

これを完了して正しく実行するには、多くの作業が必要です。もっと簡単なことには、ヘルパー関数/マクロを作成して、すでに行ったことを本質的にやっていますが、ユーザーから不快な詳細を隠すだけです。

+3

残念なことに、アセンブラ文はそのようなバイトにはマッピングされません( 'UINT32'配列は確かに正しくないので、' UINT8'または類似している必要があります)。 –

+0

あなたはそれらをマッピングできません。その中に数百の要素を持つ列挙を定義するだけです。間違いなく、結果の列挙型定数には、256以上のものがある場合は8ビット以上の定数が必要ですが、UINT32の配列に格納する際に間違いはありませんか? UINT32は、 'mov eax、-123'や' movバイトptr [ebx + 0x123]、 'a''のような定数を-123、0x123、' a 'として簡単に埋め込むことができるので便利です。あなたはおそらくその点を逃したでしょう。この配列には実行の準備ができていない命令が含まれており、解析され、余分なコードでコンパイルされるものが含まれます。 –

+1

@Alex、彼のポイントは、x86/x64アセンブラの命令は、サイズとエンコーディング形式が大きく異なることでした。これは単純なマッピングの問題ではなく、あなたのマップにconstを埋め込むことでその目的を破ります。 –

2

実際のアセンブラに依存することができます。バイナリ出力を生成するものが明らかです。 yasmまたはfasmを見ることを検討してください(一時的なアセンブリファイルを書いたり、外部プロセスを起動したり、出力ファイルを読み込んだりする必要はありませんので、DLLのバージョンについてはfasmフォーラムにいくつかの記事がありますが、それ以降のバージョンでは)。

あなたのニーズが比較的単純な場合でも、これは過剰です。私はあなたが必要とするニーモニックをサポートするC++アセンブラクラスを、GeneratePrologueGenerateEpilogueInstructionPointerRelativeAddressなどのヘルパー関数と一緒に実行することを検討したいと思います。これにより、疑似アセンブリを作成し、ヘルパー関数で32/64bitの問題を処理することができます。

+0

マシンコードを専用アセンブラファイルに分解し、実際のアセンブラに翻訳をさせるのが私の好みの解決策です。しかし、私のマシンコードは静的ではないことに注意してください - レジスタに入れられる値は実行時にしかわかりませんので、実行時に値を修正するためにアセンブラによって生成されたコードを "修正"できる必要があります。それを行う良い方法はありますか? –

+0

生成されたコードを生成するのと同じプロセスから実行する必要がありますか、別のプロセス(シェルコード注入など)の出力を生成する必要がありますか? – snemarch

+0

あります。パッチを必要とする場所のオフセットを(生成されたasmソースコードの最初または最後に)テーブルに格納し、パッチを適用する必要がある指示にダミー値を使用します。次に、そのテーブルを使用して結果のバイナリにパッチを当てます。あるいは、これらのランタイム値をテーブルに入れて、そのテーブルからasmコードをフェッチするようにしてください。 –

0

明白なことは、関心のあるマシン命令の要素の生成を表す抽象化のセットを構築し、次に必要な命令/アドレッシングモードを得るための呼び出しを作成することです。幅広い種類のコードを生成すると、このようにして命令セット全体をエンコードすることができます。

ObjectCodeEmitMovRegister32ScaledRegister32OffsetRegister32(EAX,EDX,4,-LowerBound*4,ESP); 

あなたは、私が長い名前が好きで伝えることができます:

そして、MOV命令を生成するために、あなたは次のようなコードを書くことができます。 (少なくとも彼らが何をしているか決して忘れることはありません)

私はC言語で実装されたこれをサポートしているコードジェネレータの一部です。これは、MODとSIBバイトの生成という最も難しい部分の種類を網羅しています。このスタイルに従って、好きなように多くの命令セットを実装できます。この例はx32のみに適用されるため、OPはそれに応じて拡張および変更する必要があります。 MOV命令ジェネレータの定義は最後に終了しています。

#define Register32T enum Register32Type 
enum Register32Type {EAX=0,ECX=1,EDX=2,EBX=3,ESP=4,EBP=5,ESI=6,EDI=7}; 

inline 
byte ObjectCodeEmitModRM32Register32(Register32T Register32,Register32T BaseRegister32) 
// Send ModRM32Bytes for register-register mode to object file 
{ byte ModRM32Byte=0xC0+Register32*0x8+BaseRegister32; 
    ObjectCodeEmitByte(ModRM32Byte); 
    return ModRM32Byte; 
} 

inline 
byte ObjectCodeEmitModRM32Direct(Register32T Register32) 
// Send ModRM32Bytes for direct address mode to object file 
{ byte ModRM32Byte=Register32*0x8+0x05; 
    ObjectCodeEmitByte(ModRM32Byte); 
    return ModRM32Byte; 
} 

inline 
void ObjectCodeEmitSIB(Register32T ScaledRegister32, 
      natural Scale, 
      Register32T BaseRegister32) 
// send SIB byte to object file 
// Note: Use ESP for ScaledRegister32 to disable scaling; only useful when using ESP for BASE. 
{ if (ScaledRegister32==ESP && BaseRegister32!=ESP) CompilerFault(31); 
    if  (Scale==1) ObjectCodeEmitByte((byte)(0x00+ScaledRegister32*0x8+BaseRegister32)); 
    else if (Scale==2) ObjectCodeEmitByte((byte)(0x40+ScaledRegister32*0x8+BaseRegister32)); 
    else if (Scale==4) ObjectCodeEmitByte((byte)(0x80+ScaledRegister32*0x8+BaseRegister32)); 
    else if (Scale==8) ObjectCodeEmitByte((byte)(0xC0+ScaledRegister32*0x8+BaseRegister32)); 
    else CompilerFault(32); 
} 

inline 
byte ObjectCodeEmitModRM32OffsetRegister32(Register32T Register32, 
         integer Offset, 
         Register32T BaseRegister32) 
// Send ModRM32Bytes for indexed address mode to object file 
// Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization 
{ byte ModRM32Byte; 
    if (Offset==0 && BaseRegister32!=EBP) 
{ ModRM32Byte=0x00+Register32*0x8+BaseRegister32; 
    ObjectCodeEmitByte(ModRM32Byte); 
    if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); 
} 
    else if (Offset>=-128 && Offset<=127) 
     { ModRM32Byte=0x40+Register32*0x8+BaseRegister32; 
    ObjectCodeEmitByte(ModRM32Byte); 
    if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); 
    ObjectCodeEmitByte((byte)Offset); 
     } 
    else { // large offset 
    ModRM32Byte=0x80+Register32*0x8+BaseRegister32; 
    ObjectCodeEmitByte(ModRM32Byte); 
    if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); 
    ObjectCodeEmitDword(Offset); 
    } 
    return ModRM32Byte; 
} 

inline 
byte ObjectCodeEmitModRM32OffsetScaledRegister32(Register32T Register32, 
         integer Offset, 
         Register32T ScaledRegister32, 
         natural Scale) 
// Send ModRM32Bytes for indexing by a scaled register with no base register to object file 
// Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization 
{ byte ModRM32Byte=0x00+Register32*0x8+ESP; 
    ObjectCodeEmitByte(ModRM32Byte); // MOD=00 --> SIB does disp32[index] 
    ObjectCodeEmitSIB(ScaledRegister32,Scale,EBP); 
    ObjectCodeEmitDword(Offset); 
    return ModRM32Byte; 
} 

inline 
byte ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32(Register32T Register32, 
           Register32T ScaledRegister32, 
           natural Scale, 
           integer Offset, 
           Register32T BaseRegister32) 
// Send ModRM32Bytes for indexed address mode to object file 
// Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization 
// If Scale==0, leave scale and scaled register out of the computation 
{ byte ModRM32Byte; 
    if (Scale==0) ObjectCodeEmitModRM32OffsetRegister32(Register32,Offset,BaseRegister32); 
    else if (Offset==0 && BaseRegister32!=EBP) 
{ ModRM32Byte=0x00+Register32*0x8+ESP; 
    ObjectCodeEmitByte(ModRM32Byte); 
    ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); 
} 
    else if (Offset>=-128 && Offset<=127) 
     { ModRM32Byte=0x40+Register32*0x8+ESP; 
    ObjectCodeEmitByte(ModRM32Byte); 
    ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); 
    ObjectCodeEmitByte((byte)Offset); 
     } 
    else { // large offset 
    ModRM32Byte=0x80+Register32*0x8+ESP; 
    ObjectCodeEmitByte(ModRM32Byte); 
    ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); 
    ObjectCodeEmitDword(Offset); 
    } 
    return ModRM32Byte; 
} 

inline 
void ObjectCodeEmitLeaRegister32OffsetRegister32ScaledPlusBase32(
       Register32T Register32Destination, 
          integer Offset, 
          Register32T Register32Source, 
       natural Scale, // 1,2,4 or 8 
       Register32T Base) 
// send "LEA Register32,offset[Register32*Scale+Base]" to object file 
{ ObjectCodeEmitLeaOpcode(); 
    ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32(
    Register32Destination,Register32Source,Scale,Offset,Base); 
} 

inline 
void ObjectCodeEmitMovRegister32ScaledRegister32OffsetRegister32(Register32T DestinationRegister32, 
           Register32T ScaledRegister32, 
           natural Scale, 
           integer Offset, 
           Register32T BaseRegister32) 
// Emit Mov R32 using scaled index addressing 
{ ObjectCodeEmitMovRegister32Opcode(); 
    ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32(DestinationRegister32, 
          ScaledRegister32, 
          Scale, 
          Offset, 
          BaseRegister32); 
} 
関連する問題