SetThreadContextが変な動きをする
64bitプロセスがSetThreadContextをCONTEXT_CONTROLフラグなしで行うと、対象になったスレッドのCSがなぜか勝手に0x23になる場合がある。64bitプロセスのCSは本来0x33で、0x23はWOW64(32bitプロセス)のための値である。この異常な変更が行われると、そのスレッドは32bitを超えるアドレスを正しく扱えなくなるため、正常に動作しなくなる。
手元のx64 Vista SP2/x64 XP SP2でこの異常な変更を確認できた。x64 Win7 ではこのような現象は起こらない。回避策としては、(仮に値が不要でも)常にCONTEXT_CONTROLフラグをつけ、正しい値を取得したうえでSetThreadContextする。以下は再現コード。
// // >cl generic.cpp // #include <windows.h> #include <stdio.h> #ifndef _AMD64_ #error "only work on x64" #endif DWORD WINAPI StartRoutine(LPVOID lpThreadParameter) { printf("%s running on %p\n", __FUNCTION__, (void*)StartRoutine); for (;;){} return 0; } int main(int argc, char* argv[]) { HANDLE Thread; CONTEXT Context; printf("CreateThread\n"); Thread = CreateThread(NULL, 0, StartRoutine, NULL, 0, NULL); Sleep(2000); SuspendThread(Thread); printf("GetThreadContext\n"); Context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(Thread, &Context); printf("CS = 0x%02X\n", Context.SegCs); printf("Get/SetThreadContext\n"); Context.ContextFlags = CONTEXT_INTEGER; GetThreadContext(Thread, &Context); SetThreadContext(Thread, &Context); printf("GetThreadContext\n"); Context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(Thread, &Context); printf("CS = 0x%02X\n", Context.SegCs); printf("ResumeThread\n"); ResumeThread(Thread); CloseHandle(Thread); Sleep(10000); return 0; }
>generic.exe CreateThread StartRoutine running on 0000000140001000 // 32bitを超えるアドレス空間で動作開始 GetThreadContext CS = 0x33 Get/SetThreadContext GetThreadContext CS = 0x23 // ここでなぜか0x23になってる ResumeThread // スレッドを再開するとクラッシュ
なにこれこわい。