13

これは、モード切り替えを行うことで64ビットプロセスで32ビットコードを実行することは可能ですか?の正確な複製である可能性があり ます。、しかし、その質問は1年前のものであり、ソースコードを提供しない回答は1つだけです。もっと詳しい答えを期待しています。

私は64ビットLinuxを実行しています(重要な場合はUbuntu 12.04)。ページを割り当て、64ビットコードを書き込み、そのコードを実行するコードを次に示します。

#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h>  // mprotect
#include <unistd.h>  // sysconf

unsigned char test_function[] = { 0xC3 };  // RET
int main()
{
    int pagesize = sysconf(_SC_PAGE_SIZE);
    unsigned char *buffer = memalign(pagesize, pagesize);
    void (*func)() = (void (*)())buffer;

    memcpy(buffer, test_function, sizeof test_function);

    // func();  // will segfault 
    mprotect(buffer, pagesize, PROT_EXEC);
    func();  // works fine
}

さて、純粋に娯楽的価値のために、私は同じことをしたいと思いますがbuffer、64ビットコードの代わりに任意の32ビット(ia32)コードを含みます。このページは、CSセグメント記述子のビットをに設定することにより、「長い互換性サブモード」に入ると、64ビットプロセッサで32ビットコードを実行できることを意味しますLMA=1, L=0, D=1。このセットアップを実行するプロローグ/エピローグで32ビットコードをラップするつもりです。

しかし、Linuxでユーザーモードでこのセットアップを行うことはできますか?(BSD / Darwinの回答も受け入れられます。)これは、私が概念について本当にぼんやりし始めたところです。lcall解決策には、新しいセグメント記述子をGDT(またはLDTですか?)に追加し、命令を介してそのセグメントに切り替えることが含まれると思います。しかし、それはすべてユーザーモードで実行できますか?

これは、互換性サブモードで正常に実行された場合に4を返し、ロングモードで実行された場合に8を返すサンプル関数です。%rax=4私の目標は、カーネルモードに陥ることなく(または文書化されたシステムコールを介してのみそうすることなく)、このコードパスを取得して反対側に出てくるように命令ポインターを取得することです。

unsigned char behave_differently_depending_on_processor_mode[] = {
    0x89, 0xE0,  // movl %esp, %eax
    0x56,        // push %{e,r}si
    0x29, 0xE0,  // subl %esp, %eax
    0x5E,        // pop %{e,r}si
    0xC3         // ret
};
4

1 に答える 1

11

はい、できます。完全にサポートされているインターフェイスを使用して実行することもできます。modify_ldt を使用して 32 ビット コード セグメントを LDT にインストールし、32 ビット コードへの far ポインターを設定してからljumpl *(%eax)、AT&T 表記を使用して間接的にジャンプします。

ただし、あらゆる種類のスナフスに直面することになります。スタック ポインターの上位ビットが破壊される可能性があります。実際のコードを実際に実行したい場合は、おそらくデータ セグメントが必要です。また、64 ビット モードに戻すには、さらに遠くまでジャンプする必要があります。

完全に完成した例は、私のlinux-clock-testsにありtest_vsyscall.ccます。(リリースされたカーネルでは少し壊れています。クラッシュします。int cc「nop」など、より賢いものに変更する必要があります。.intcc32

于 2012-11-13T04:38:38.823 に答える