2

x86およびamd64用のトランポリンを作成して、特定の関数呼び出しが既知のメモリ位置に格納されているアドレスにすぐにベクトル化されるようにしようとしています(目的は、最初のターゲットアドレスが特定のDLL(Windows)内にあることを確認することです)。

次のコードは_fn、実際のターゲット アドレスを開始するメモリ ロケーション (またはそれらのグループ) として使用しようとしています。

(*_fn[IDX])(); // rough equivalent in C

.globl _asmfn
_asmfn:
  jmp *_fn+8*IDX(%rip)

IDX、いくつかの CPP マクロを使用して構築され、それぞれ_fnが関数ポインターの配列内のスロットに一意にマップされるさまざまな埋め込み DLL ベクトルを提供することを目的としています。これは単純なテスト プログラムで動作しますが、実際に共有ライブラリに配置すると (現時点では OSX でテストします)、_asmfn コードにベクトルしようとするとバス エラーが発生します。

Invalid memory access of location 0x10aa1f320 rip=0x10aa1f320

このコードの最終的なターゲットは Windows ですが、まだ試したことはありません (最初に OSX/intel のテスト ケースで少なくともアセンブリを証明できると考えました)。amd64 ジャンプは、少なくとも名目上は正しいですか、それとも何かを見逃していますか?

amd64 のトランポリンに関する参考資料です。

編集

ジャンプWindows 7で適切に機能します(最終的にテストする機会を得ました)。ただし、OSXで失敗する理由を知りたいと思っています。バス エラーは KERN_PROTECTION_FAILURE によって引き起こされます。これは、OS の保護がそのコードの実行を妨げていることを示しているように見えます。ターゲット アドレス割り当てられたメモリ (libffi によって生成されたトランポリンです) ですが、実行可能メモリとして適切にマークされていると思います。それが実行可能メモリの問題である場合、それは私のスタンドアロン テスト コードが機能する理由を説明します (コールバック トランポリンはコンパイルされ、割り当てられません)。

4

2 に答える 2

0

PC相対アドレス指定を使用する場合、オフセットは±2GB以内でなければならないことに注意してください。つまり、ジャンプテーブルとトランポリンが互いに離れすぎてはいけないということです。トランポリン自体に関して、レジスターを壊さずに転送するためにWindowsx64で実行できることは次のとおりです。

  1. シーケンス:
    PUSH <high32>
    MOV DWORD PTR [ RSP - 4 ], <low32>
    RET
    これは、Win64とUN *Xx86_64の両方で機能します。UN * Xではありますが、関数がレッドゾーンを使用している場合は、混乱しています...

  2. シーケンス:
    JMP [ RIP ]
    .L: <tgtaddr64>
    ここでも、Win64とUN *Xx86_64の両方に適用できます。

  3. シーケンス:これは、Win64 ABIによって予約された(スタックのリターンアドレスのすぐ
    MOV DWORD PTR [ RSP + c ], <low32>
    MOV DWORD PTR [ RSP + 8 ], <high32>
    JMP [ RSP + 8 ]
    に ある)32バイトの「引数スペース」の一部を(ab)使用するため、Win64固有です。これに相当するUN*X x86_64は、そこで予約されている128バイトの「レッドゾーン」の一部(スタックのリターンアドレスのすぐ)を(乱用)使用することです。 どちらも、クローバー(上書き)が許容できる場合にのみ使用できます。トランポリンを呼び出した時点で何が入っていますか。
    MOV DWORD PTR [ RSP - c ], <low32>
    MOV DWORD PTR [ RSP - 8 ], <high32>
    JMP [ RSP - 8 ]

このような位置に依存しないレジスターニュートラルなトランポリンをメモリ内に直接構築できる場合は、次のようになります(方法1の場合)。

#include <stdint.h>
#include <stdio.h>

char *mystr = "Hello, World!\n";

int main(int argc, char **argv)
{
    struct __attribute__((packed)) {
                char PUSH;
                uint32_t CONST_TO_PUSH;
                uint32_t MOV_TO_4PLUS_RSP;
                uint32_t CONST_TO_MOV;
                char RET;
    } mycode = {
                0x68, ((uint32_t)printf),
                0x042444c7, (uint32_t)((uintptr_t)printf >> 32),
                0xc3
    };
    void *buf = /* fill in an OS-specific way to get an executable buffer */;
    memcpy(buf, &mycode, sizeof(mycode));

    __asm__ __volatile__(
        "push $0f\n\t"         // this is to make the "jmp" return
        "jmp *%0\n\t"
        "0:\n\t" : : "r"(buf), "D"(mystr), "a"(0));

    return 0;
}

これは、「呼び出された」関数によって不揮発性レジスタが破壊されているかどうかを考慮していないことに注意してください。また、トランポリンバッファーを実行可能にする方法も省略しました(通常、スタックはWin64 / x86_64にはありません)。

于 2012-08-14T16:55:21.843 に答える
0

@HarryJohnston にはその権利があり、パーミッションの問題は OS X でのみ発生しました。コードは、ターゲットの Windows 環境で正常に動作します。

于 2013-01-01T22:36:09.537 に答える