私の質問は少し奇妙に思えるかもしれませんが、これで終わりです。私は C++ でプログラムを動的に作成しようとしています (主に楽しみのためですが、プログラム上の理由でもあります)。思ったほど難しくはありません。これを行うには、次のように実行時にアセンブリを使用する必要があります。
byte * buffer = new byte[5];
*buffer = '0xE9'; // Code for 'jmp'
*(uint*)(buffer + 1) = 'address destination'; // Address to jump to
1 つのプラットフォームとコンパイラのみをターゲットにしているため、これは思ったよりもはるかに簡単です。Linux 32 ビットの GCC (および1 つの呼び出し規約 cdecl)。そのため、トリガーからの呼び出しをリダイレクトする動的アセンブリ関数を作成しようとしているので、クラスメソッドをコールバックとして使用できます (C API ライブラリでも (もちろん cdecl を使用))。これは、ポインターとネイティブ型 (char、int、short など) をサポートするためだけに必要です。
ANYTHING MyRedirect(ANY AMOUNT ARGUMENTS)
{
return MyClassFunc('this', ANY AMOUNT ARGUMENTS);
}
上記の関数は、純粋なアセンブリ (C++ のメモリ内) で作成したい関数です。関数は非常に単純なので、その ASM も単純です (引数によって異なります)。
55 push %ebp
89 e5 mov %esp,%ebp
83 ec 04 sub $0x4,%esp
8b 45 08 mov 0x8(%ebp),%eax
89 04 24 mov %eax,(%esp)
e8 00 00 00 00 call <address>
c9 leave
c3 ret
そのため、私のプログラムでは、ASM パターン ジェネレーターを作成しました (ASM は特によく知らないので、パターンを検索します)。この関数は、関数が必要とする引数の量を指定することにより、アセンブリ コード (バイト単位、上記の正確なケース、つまり、リダイレクトして戻る関数) を生成できます。これは私の C++ コードの抜粋です。
std::vector<byte> detourFunc(10 + stackSize, 0x90); // Base is 10 bytes + argument size
// This becomes 'push %ebp; move %esp, %ebp'
detourFunc.push_back(0x55); // push %ebp
detourFunc.push_back(0x89); // mov
detourFunc.push_back(0xE5); // %esp, %ebp
// Check for arguments
if(stackSize != 0)
{
detourFunc.push_back(0x83); // sub
detourFunc.push_back(0xEC); // %esp
detourFunc.push_back(stackSize); // stack size required
// If there are arguments, we want to push them
// in the opposite direction (cdecl convention)
for(int i = (argumentCount - 1); i >= 0; i--)
{
// This is what I'm trying to implement
// ...
}
// Check if we need to add 'this'
if(m_callbackClassPtr)
{
}
}
// This is our call operator
detourFunc.push_back(0xE8); // call
// All nop, this will be replaced by an address
detourFunc.push_back(0x90); // nop
detourFunc.push_back(0x90); // nop
detourFunc.push_back(0x90); // nop
detourFunc.push_back(0x90); // nop
if(stackSize == 0)
{
// In case of no arguments, just 'pop'
detourFunc.push_back(0x5D); // pop %ebp
}
else
{
// Use 'leave' if we have arguments
detourFunc.push_back(0xC9); // leave
}
// Return function
detourFunc.push_back(0xC3); // ret
ゼロを指定すると、stackSize
これが出力になります。
55 push %ebp
89 e5 mov %esp,%ebp
e8 90 90 90 90 call <address>
5d pop %ebp
c3 ret
ご覧のとおり、これは完全に有効な 32 ビット ASM であり、引数がなく、「this」ポインターが必要ない場合、「MyRedirect」として機能します。問題は、「リダイレクト」関数が受け取るように指定した引数の量に応じて、ASM コードを生成する部分を実装したいということです。私の小さなC++プログラムでこれを成功させました(パターンをクラックしました)。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
int val = atoi(argv[1]);
printf("\tpush %%ebp\n");
printf("\tmov %%esp,%%ebp\n");
if(val == 0)
{
printf("\tcall <address>\n");
printf("\tpop %%ebp\n");
}
else
{
printf("\tsub $0x%x,%%esp\n", val * sizeof(int));
for(int i = val; i > 0; i--)
{
printf("\tmov 0x%x(%%ebp),%%eax\n", i * sizeof(int) + sizeof(int));
printf("\tmov %%eax,0x%x(%%esp)\n", i * sizeof(int) - sizeof(int));
}
printf("\tcall <address>\n");
printf("\tleave\n");
}
printf("\tret\n");
return 0;
}
この関数は、「objdump」によって生成された ASM コードとまったく同じパターンを出力します。私の質問は次のとおりです。上記のようなリダイレクト関数のみが必要な場合、引数に関係なく、Linux 32ビットのみである場合、または知っておく必要がある落とし穴がある場合、これはすべての場合に有効ですか? 例えば; 生成された ASM は「shorts」または「chars」で異なりますか、それともこれは機能しますか (私は整数でのみテストしました)、また「void」を返す関数を呼び出すと (ASM にどのように影響しますか)?
私はすべてを少しあいまいに説明したかもしれないので、誤解の代わりに質問してください:)
注: 代替案を知りたくありません。現在の実装を楽しんでおり、非常に興味深い実装だと思います。この件に関するご支援をいただければ幸いです。
編集: 興味がある場合は、上記の C++ コードのダンプをいくつか示します:リンク