59

ネイティブの機械語コードを呼び出そうとしています。これが私がこれまでに持っているものです(バスエラーが発生します):

char prog[] = {'\xc3'}; // x86 ret instruction

int main()
{
    typedef double (*dfunc)();

    dfunc d = (dfunc)(&prog[0]);
    (*d)();
    return 0;
}

関数を正しく呼び出し、ret 命令に到達します。しかし、ret 命令を実行しようとすると、SIGBUS エラーが発生します。実行のためにクリアされていないページでコードを実行しているためですか?

それで、私はここで何が間違っていますか?

4

6 に答える 6

52

prog が存在するページを実行可能にするには、memprotect を呼び出す必要があります。次のコードはこの呼び出しを行い、prog でテキストを実行できます。

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

char prog[] = {
   0x55,             // push   %rbp
   0x48, 0x89, 0xe5, // mov    %rsp,%rbp
   0xf2, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00,
       //movsd  0x0(%rip),%xmm0        # c <x+0xc>
   0x00,
   0x5d,             // pop    %rbp
   0xc3,             // retq
};

int main()
{
    long pagesize = sysconf(_SC_PAGE_SIZE);
    long page_no = (long)prog/pagesize;
    int res = mprotect((void*)(page_no*pagesize), (long)page_no+sizeof(prog), PROT_EXEC|PROT_READ|PROT_WRITE);
    if(res)
    {
        fprintf(stderr, "mprotect error:%d\n", res);
        return 1;
    }
    typedef double (*dfunc)(void);

    dfunc d = (dfunc)(&prog[0]);
    double x = (*d)();
    printf("x=%f\n", x);
    fflush(stdout);
    return 0;
}
于 2016-10-05T08:01:55.320 に答える
31

誰もが既に言ったように、prog[]実行可能であることを確認する必要がありますが、それを行う適切な方法は、JIT コンパイラを作成していない限り、リンカー スクリプトを使用するか、セクションを指定して、シンボルを実行可能領域に配置することですコンパイラが許可する場合は C コード。

const char prog[] __attribute__((section(".text"))) = {...}
于 2016-10-05T10:57:55.203 に答える
30

ほとんどすべての C コンパイラで、通常のアセンブリ言語をコードに埋め込むことでこれを行うことができます。もちろん、これは C に対する非標準の拡張機能ですが、コンパイラの作成者は、これがしばしば必要になることを認識しています。非標準の拡張機能であるため、コンパイラのマニュアルを読んでその方法を確認する必要がありますが、GCC の "asm" 拡張機能はかなり標準的なアプローチです。

 void DoCheck(uint32_t dwSomeValue)
 {
    uint32_t dwRes;

    // Assumes dwSomeValue is not zero.
    asm ("bsfl %1,%0"
      : "=r" (dwRes)
      : "r" (dwSomeValue)
      : "cc");

    assert(dwRes > 3);
 }

アセンブラーではスタックを破棄するのは簡単なので、コンパイラーでは、アセンブラーの一部として使用するレジスターを特定することもできます。その後、コンパイラは、その関数の残りの部分がそれらのレジスタを確実に回避できるようにします。

アセンブラ コードを自分で記述している場合、そのアセンブラをバイト配列として設定する正当な理由はありません。これは単なるコードのにおいではありません。C にアセンブラーを埋め込む正しい方法である「asm」拡張子を認識していない場合にのみ発生する可能性のある本物のエラーだと思います。

于 2016-10-05T17:17:28.753 に答える
9

基本的に、これはウイルス作成者への公開の招待状であったため、取り締まりが行われました。しかし、ストレート C のネイティブ マシンコードを使用して、割り当て、バッファリング、およびセットアップを行うことができます。それは問題ありません。問題はそれを呼び出すことです。バッファのアドレスで関数ポインタを設定して呼び出すことはできますが、それがうまくいく可能性はほとんどなく、どうにかしてやりたいことをうまくやらせることができれば、コンパイラの次のバージョンで壊れる可能性が非常に高くなります。 . したがって、最適な方法は、単純にインライン アセンブリを使用して、リターンをセットアップし、自動生成されたコードにジャンプすることです。ただし、システムがこれに対して保護する場合、ルディが回答で説明したように、保護を回避する方法を見つける必要があります (ただし、特定のシステムに非常に固有です)。

于 2016-10-05T10:42:18.113 に答える
6

明らかなエラーの 1 つは、返されていると主張している を\xc3返していないdoubleことです。

于 2016-10-05T07:11:34.813 に答える