7

私は(C ++)プロジェクトに取り組んでいます。これには、完全に動的に割り当てられた関数が必要です。つまり、malloc / newとmprotectを使用してから、バッファーを手動でアセンブリコードに変更します。このため、他の_cdecl関数の複製であるために、この「バッファー」に何が必要かを正確に知りました。例えば:

int ImAcDeclFunc(int a, int b)
{
     return a + b;
}

この関数の複製を文字通り作成したいが、完全に動的に作成したい場合、何が必要になりますか(インラインアセンブリを備えたC ++であることを覚えておいてください)?手始めに、私はこのようなこと(または同様の解決策)をしなければならないと思います:

// My main....
byte * ImAcDeclFunc = new byte[memory];
mprotect(Align(ImAcDeclFunc), pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);

この後、のアセンブリコードを見つける必要がありますImAcDeclFunc(int a, int b);。今でも私は組み立てがお粗末ですが、この関数はAT&T構文でどのようになりますか?これが私の大胆な試みです:

push %ebp
movl %%ebp, %%esp
movl 8(%ebp), %%eax
movl 12(%ebp), %%edx
addl edx, eax
pop ebp
ret

このコードが正しい場合(私は非常に疑わしいので、訂正してください)、このコードの値を16進数で見つけるだけでよく(たとえば、「jmp」は0xE9、「inc」は0xFE)、これらの値を直接使用します。 C ++?以前のC++コードを続行する場合:

*ImAcDeclFunc = 'hex value for push'; // This is 'push' from the first line
*(uint)(ImAcDeclFunc + 1) = 'address to push'; // This is %ebp from the first line
*(ImAcDeclFunc + 5) = 'hex value for movl' // This is movl from the second line
// and so on...

コード/バッファー全体に対してこれを実行した後、完全に動的な_cdecl関数にはこれで十分でしょうか(つまり、関数ポインターにキャストして実行できint result = ((int (*)(int, int))ImAcDeclFunc)(firstArg, secondArg)ますか?)。そして、私はboost :: functionやそれに類似したものを使用することに興味がありません。関数が完全に動的である必要があるため、私の興味は:)

注:この質問は私の前の質問の続きですが、はるかに詳細です。

4

3 に答える 3

6

あなたがこれを取る場合lala.c

int ImAcDeclFunc(int a, int b)
{
    return a + b;
}

int main(void)
{
    return 0;
}

でコンパイルできますgcc -Wall lala.c -o lala。次に、を使用して実行可能ファイルを逆アセンブルできますobjdump -Dslx lala >> lala.txtImAcDeclFunc次のように組み立てられています。

00000000004004c4 <ImAcDeclFunc>:
ImAcDeclFunc():
  4004c4:   55                      push   %rbp
  4004c5:   48 89 e5                mov    %rsp,%rbp
  4004c8:   89 7d fc                mov    %edi,-0x4(%rbp)
  4004cb:   89 75 f8                mov    %esi,-0x8(%rbp)
  4004ce:   8b 45 f8                mov    -0x8(%rbp),%eax
  4004d1:   8b 55 fc                mov    -0x4(%rbp),%edx
  4004d4:   8d 04 02                lea    (%rdx,%rax,1),%eax
  4004d7:   c9                      leaveq 
  4004d8:   c3                      retq   

実際、この関数は他の場所に比較的簡単にコピーできます。この場合、バイトをコピーでき、それでうまくいくと言っているのは完全に正しいです。

オペコードの一部として相対オフセットを使用する命令を使用し始めると、問題が発生します。たとえば、相対ジャンプまたは相対呼び出し。このような場合、元の場所と同じアドレスに命令をコピーできない限り、命令を適切に再配置する必要があります。

簡単に言うと、再配置するには、元のベースの場所を見つけ、ベースとなる場所との差を計算し、このオフセットに関して各相対命令を再配置する必要があります。これ自体は実行可能です。あなたの本当の難しさは、他の関数への呼び出し、特にライブラリへの関数呼び出しを処理することです。この場合、ライブラリがリンクされていることを保証してから、ターゲットとする実行可能形式で定義された方法でライブラリを呼び出す必要があります。これは非常に重要です。あなたがまだ興味を持っているなら、私はあなたがこれについて読むべき場所の方向にあなたを向けることができます。


上記の単純なケースでは、これを行うことができます。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/mman.h>
#include <unistd.h>

int main(void)
{
    char func[] = {0x55, 0x48, 0x89, 0xe5, 0x89, 0x7d, 0xfc,
    0x89, 0x75, 0xf8, 0x8b, 0x45, 0xf8,
    0x8b, 0x55, 0xfc, 0x8d, 0x04, 0x02,
    0xc9, 0xc3};

    int (* func_copy)(int,int) = mmap(NULL, sizeof(func),
        PROT_WRITE | PROT_READ | PROT_EXEC,
        MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);

    memcpy(func_copy, func, sizeof(func));
    printf("1 + 2 = %d\n", func_copy(1,2));

    munmap(func_copy, sizeof(func));
    return EXIT_SUCCESS;
}

これはx86-64で正常に機能します。それは印刷します:

1 + 2 = 3
于 2012-05-04T21:57:20.853 に答える
1

GNU lightningをチェックすることをお勧めします:http ://www.gnu.org/software/lightning/ 。それはあなたがやろうとしていることであなたを助けるかもしれません。

于 2012-05-04T21:45:35.790 に答える
1

自己修正プログラムを作成するよりも、プロジェクトにスクリプト言語を組み込む方がよいと思います。時間がかからず、柔軟性が高まります。

この関数の複製を文字通り作成したいが、完全に動的に作成したい場合、何が必要になりますか(インラインアセンブリを備えたC ++であることを覚えておいてください)?

それは逆アセンブラを持った人間を必要とするでしょう。技術的には、関数は1つのアドレスで開始し、returnステートメントで終了する必要があります。ただし、最適化フェーズでコンパイラが関数をどのように処理したかは不明です。関数のエントリポイントがなんらかの奇妙な場所(関数の終わり、returnステートメントの後など)にある場合や、関数が他の関数と共有される複数の部分に分割されている場合でも、私は驚かないでしょう。

于 2012-05-04T23:03:29.847 に答える