WOW64からのシステムコール概要 のまとめ

要点をコードで。Win7(RC)x64でのみ動くので、あくまで例示ということで。

まとめ1:eaxにシステムコール番号、ecxにディスパッチ関数番号を入れて、call dword ptr fs:[0C0h] するとWOW64からシステムコールを発行できる

このプログラムは、自分でWOW64のNtGetCurrentProcessorNumberを定義して実行する。

// call dword ptr fs:[0C0h]
// add esp, 4
#define call_X86SwitchTo64BitMode \
    __asm _emit 0x64              \
    __asm _emit 0xff              \
    __asm _emit 0x15              \
    __asm _emit 0xc0              \
    __asm _emit 0x00              \
    __asm _emit 0x00              \
    __asm _emit 0x00              \
    __asm add esp, 4              \


__declspec(naked)
ULONG NtGetCurrentProcessorNumber()
{
  __asm 
  { 
    mov eax, 0CBh
    mov ecx, 19h    
    call_X86SwitchTo64BitMode
    ret
  }
}

int _tmain()
{
  if (::IsDebuggerPresent()) __debugbreak();

  SetProcessAffinityMask(GetCurrentProcess(), 1);
  std::cout << NtGetCurrentProcessorNumber() << std::endl;

  SetProcessAffinityMask(GetCurrentProcess(), 2);
  std::cout << NtGetCurrentProcessorNumber() << std::endl;
  return 0;
}
0
1

まとめ2:0x33セグメントへのジャンプでx64モードに切り替わり、0x23セグメントへのジャンプでx86モードに切り替わる

このプログラムは、自力でx64へスイッチしてネイティブNtGetCurrentProcessorNumberを呼び出す。

//
// Visual Studioのデバッガでは追いかけることができないです。WinDbgをつかってね
//
int _tmain()
{
  BYTE x86SwitchTo64BitModeCode[] = 
  {
    0xea, 0xff, 0xff, 0xff, 0xff, 0x33, 0x00,       // jmp 0033h:ffffffffh  (オフセット部は実行時に書き換え)
  };
  BYTE x64SwitchTo32BitModeCode[] =
  {
    0x90,                                           // nop          (or int 3)
    0xb8, 0xcb, 0x00, 0x00, 0x00,                   // mov eax,0CBh (ntdll!NtGetCurrentProcessorNumber)
    0x0f, 0x05,                                     // syscall
    0x41, 0xc7, 0x46, 0x04, 0x23, 0x00, 0x00, 0x00, // mov dword ptr [r14+4], 23h
    0x41, 0xc7, 0x46, 0x00, 0xff, 0xff, 0xff, 0xff, // mov dword ptr [r14], ffffffffh  (オフセット部は実行時に書き換え)
    0x41, 0xff, 0x2e,                               // jmp fword ptr [r14]
  };
  BYTE x86ReturnCode[] = { 0xc3, };                 // ret

  if (::IsDebuggerPresent()) __debugbreak();

  BOOL result = FALSE;
  DWORD old_protect = 0;

  // スタブにするアドレスを取得
  //  スタックやヒープからコードを実行するのもあれなので
  //  ここはアグレッシブにAPIコードを書き換えてそれを実行してみる(破壊的)
  FARPROC x86Return            = GetProcAddress(GetModuleHandle(_T("kernel32")),   "GetEnvironmentStringsA");
  FARPROC x86SwitchTo64BitMode = GetProcAddress(GetModuleHandle(_T("kernel32")),   "GetEnvironmentStringsW");
  FARPROC x64SwitchTo32BitMode = GetProcAddress(GetModuleHandle(_T("kernelbase")), "GetEnvironmentStringsW");

  // ちゃんと取得できていて、かつ重複していないこと
  _ASSERT(x86Return && x86SwitchTo64BitMode && x64SwitchTo32BitMode 
       && x86Return != x86SwitchTo64BitMode && x86Return != x64SwitchTo32BitMode && x86SwitchTo64BitMode != x64SwitchTo32BitMode);

  // コードのオフセット部を構築
  memcpy(&x86SwitchTo64BitModeCode[1],  &x64SwitchTo32BitMode, sizeof(x64SwitchTo32BitMode));
  memcpy(&x64SwitchTo32BitModeCode[20], &x86Return,            sizeof(x86Return));

  // x64へのジャンプコードを書き込み
  result = VirtualProtect(x86SwitchTo64BitMode, sizeof(x86SwitchTo64BitModeCode), PAGE_EXECUTE_READWRITE, &old_protect);
  memcpy(x86SwitchTo64BitMode, x86SwitchTo64BitModeCode, sizeof(x86SwitchTo64BitModeCode));
  result = VirtualProtect(x86SwitchTo64BitMode, sizeof(x86SwitchTo64BitModeCode), old_protect, &old_protect);
  std::cout << "x86 to x64 code: (" << x86SwitchTo64BitMode << "h)\n"
            << "               : jmp 0x0033:" << x64SwitchTo32BitMode << "h\n";

  // x86へのジャンプコードを書き込み
  result = VirtualProtect(x64SwitchTo32BitMode, sizeof(x64SwitchTo32BitModeCode), PAGE_EXECUTE_READWRITE, &old_protect);
  memcpy(x64SwitchTo32BitMode, x64SwitchTo32BitModeCode, sizeof(x64SwitchTo32BitModeCode));
  result = VirtualProtect(x64SwitchTo32BitMode, sizeof(x64SwitchTo32BitModeCode), old_protect, &old_protect);
  std::cout << "x64 to x86 code: (" << x64SwitchTo32BitMode << "h)\n"
            << "               : nop                   ; or int 3h\n"
            << "               : mov eax,0CBh          ; ntdll!NtGetCurrentProcessorNumber\n"
            << "               : syscall\n"
            << "               : mov dword ptr [r14+4], 23h\n"
            << "               : mov dword ptr [r14], " << x86Return << "h\n"
            << "               : jmp fword ptr [r14]\n";

  // callしてx64へいくのでx86に戻ってきたときにretを
  result = VirtualProtect(x86Return, sizeof(x86ReturnCode), PAGE_EXECUTE_READWRITE, &old_protect);
  memcpy(x86Return, x86ReturnCode, sizeof(x86ReturnCode));
  result = VirtualProtect(x86Return, sizeof(x86ReturnCode), old_protect, &old_protect);
  std::cout << "x86 ret code   : (" << x86Return << "h)\n"
            << "               : ret\n";

  SetProcessAffinityMask(GetCurrentProcess(), 1);

  // x64にいってネイティブのNtGetCurrentProcessorNumberを呼んでくるでござる!
  if (::IsDebuggerPresent()) __debugbreak();
  DWORD CurrentProcessorNumber1 = x86SwitchTo64BitMode();
  if (::IsDebuggerPresent()) __debugbreak();

  SetProcessAffinityMask(GetCurrentProcess(), 2);
  DWORD CurrentProcessorNumber2 = x86SwitchTo64BitMode();
  std::cout << CurrentProcessorNumber1 << "\n" << CurrentProcessorNumber2 << std::endl;
  return 0;
}


出力。

x86 to x64 code: (756C5849h)
               : jmp 0x0033:74CADD77h
x64 to x86 code: (74CADD77h)
               : nop                   ; or int 3h
               : mov eax,0CBh          ; ntdll!NtGetCurrentProcessorNumber
               : syscall
               : mov dword ptr [r14+4], 23h
               : mov dword ptr [r14], 756CF360h
               : jmp fword ptr [r14]
x86 ret code   : (756CF360h)
               : ret
0
1


WinDbgによるアセンブルコード表示。

; 1
kernel32!GetEnvironmentStringsWStub:
756c5849 ea77ddca743300  jmp     0033:74CADD77

; 2(ここでx64になってる)
KERNELBASE!GetEnvironmentStringsW:
00000000`74cadd77 90               nop
00000000`74cadd78 b8cb000000       mov     eax,0CBh
00000000`74cadd7d 0f05             syscall
00000000`74cadd7f 41c7460423000000 mov     dword ptr [r14+4],23h
00000000`74cadd87 41c7460060f36c75 mov     dword ptr [r14],offset kernel32!GetEnvironmentStrings (00000000`756cf360)
00000000`74cadd8f 41ff2e           jmp     fword ptr [r14]


; 3(x86にもどってる)
kernel32!GetEnvironmentStrings:
756cf360 c3              ret

おk(ただし地味)