3

Windows 7 X64 (SP1) で syscall 命令がどのように機能するかをシミュレートしようとしたので、MinGW64 で 64 ビット GCC の例をプログラムします。私が知っているように、Windows の場合、すべての syscall エントリ ポイントは ntdll.dll または ntdll32.dll 内にあります (この場合は、ntdll.dll だけを扱います)。

Status = NtCreateFile(&FileHandle,                      // returned file handle
                      (GENERIC_WRITE | SYNCHRONIZE),    // desired access
                      &ObjectAttributes,                // ptr to object attributes
                      &Iosb,                            // ptr to I/O status block
                      0,                                // allocation size
                      FILE_ATTRIBUTE_NORMAL,            // file attributes
                      0,                                // share access
                      FILE_SUPERSEDE,                   // create disposition
                      FILE_SYNCHRONOUS_IO_NONALERT,     // create options
                      NULL,                             // ptr to extended attributes
                      0);                               // length of ea buffer

これは C で書かれたソース コードの元の部分で、それを Gas で書き直しました。

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "movq   %%r9, 0x18(%%rsp)\n\t"
    "movq   %%r8, 0x10(%%rsp)\n\t"
    "movq   %%rdx, 0x8(%%rsp)\n\t"
    "movq   %%rcx, (%%rsp)\n\t"
    "movq   __imp_NtCreateFile(%%rip), %%rax\n\t"
    "call   *%%rax\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

これまでのところ、プログラムは期待どおりに動作します。テキスト ファイルを作成し、そのファイルに何かを書き込みます。

Windbg を使用して ntdll!NtCreateFile を逆アセンブルし、見ただけです (GAS AT&T 形式に書き直しました)。

    "movq   $0x52, %%rax\n\t"
    "movq   %%rcx, %%r10\n\t"
    "syscall\n\t"
    "ret\n\t"

プログラム内のコードのこの部分を次のように追加しました

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "movq   %%r9, 0x18(%%rsp)\n\t"
    "movq   %%r8, 0x10(%%rsp)\n\t"
    "movq   %%rdx, 0x8(%%rsp)\n\t"
    "movq   %%rcx, (%%rsp)\n\t"
    "movq   $0x52, %%rax\n\t"
    "movq   %%rcx, %%r10\n\t"
    "syscall\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

現在、ステータスは常に値「0xc000000d」で返され、プログラムは失敗しました。今、私はいくつかの混乱した質問があります:

  1. ユーザー モード スタックに格納されたパラメーターがカーネル モードにどのように渡されるのか? NtDll!NtCreateFile 内では何も行われていないことがわかります。

  2. %%rax に戻す正しい戻り値は? この部分はdisassmeblerにもありません。

  3. 直接システムコールを実行するときにコードを期待どおりに機能させる方法は?

大変お世話になりました。


OK、ここに作業コードを表示します

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "push $_end \n\t"
    "movq  %%rcx,%%r10\n\t"
    "movq  $0x52,%%rax\n\t"
    "syscall\n\t"
    "ret\n\t"
    "_end:\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

call/ret をシミュレートするのはそれほど苦痛ではありません。ここでは、Linus が Linux 0.11 で使用した回避策を使用しました。

4

1 に答える 1

3

スタックの深さに関してあなたは間違っていると思います。引数の多くはスタック経由で渡されます。ライブラリ呼び出しが中間にある場合、システムコールはそれらが正確にどこにあるかを期待します。

ライブラリ呼び出しをスキップして自分で syscall を実行すると (生産的なものではなく、実験のためにのみ行う必要があります)、スタックに 1 つの項目がありません。

したがって、ダミーの値をスタックにプッシュするか、オフセットを調整してください。

詳細には、元のコードでは次のことが行われます。

  • 引数をスタックに入れます (最大movq %%rcx, (%%rsp))。
  • を実行call__imp_NtCreateFileます。これにより、戻りアドレスがスタックに置か%tipれ、ライブラリ関数への転送が実行されます。
  • 次に、基本的にライブラリ関数が syscall を実行します。
  • カーネルは、スタックの一番上から 1 項目離れたデータを予期します。これは、前述の呼び出しが 1 つの項目を追加したためです。

自分で syscall を実行する場合は、カーネルのスタック ビューを移動するこの戻りアドレスを補うために、別のアイテムを配置する必要があります。

于 2013-08-21T06:27:24.360 に答える