ユーザー空間プログラムは、64 ビット Windows (現在 XP-64) で "GS:" をどのように構成できますか?
(設定により、GS:0 を任意の 64 ビット リニア アドレスに設定します)。
「JIT」環境を、もともと Win32 用に開発された X86-64 に移植しようとしています。
残念な設計上の側面の 1 つは、同一のコードを複数のユーザー空間スレッド (「ファイバー」など) で実行する必要があることです。コードの Win32 バージョンは、これに GS セレクターを使用し、ローカル データにアクセスするための適切なプレフィックスを生成します。「mov eax,GS:[offset]」は、現在のタスクの正しいデータを指します。Win32 バージョンのコードは、機能する値を持っていれば、その値を GS にロードします。
これまでのところ、64 ビット Windows は LDT をサポートしていないことがわかっているため、Win32 で使用される方法は機能しません。ただし、X86-64 命令セットには「SWAPGS」と、従来のセグメンテーションを使用せずに GS をロードする方法が含まれていますが、これはカーネル空間でのみ機能します。
X64 のマニュアルによると、たとえ Win64 が記述子へのアクセスを許可していたとしても (そうではありません)、セグメント ベースの上位 32 ビットを設定する方法はありません。これらを設定する唯一の方法は、GS_BASE_MSR を使用することです (および対応する FS_BASE_MSR - 64 ビット モードでは他のセグメント ベースは無視されます)。WRMSR命令はRing0なので直接使えません。
ユーザー空間の "GS:" を変更できる Zw* 関数、または Windows API のその他のダーク コーナーを期待しています。Windows はまだ FS: を独自の TLS に使用していると思いますが、何らかのメカニズムが利用可能でなければなりませんか?
このサンプル コードは、問題を示しています。バイト コードを使用していることをあらかじめお詫び申し上げます。VS は 64 ビット コンパイルのインライン アセンブリを行いません。説明のために、これを 1 つのファイルとして保持しようとしていました。
XP-32 では「PASS」と表示されますが、XP-x64 では表示されません。
#include <windows.h>
#include <string.h>
#include <stdio.h>
unsigned char GetDS32[] =
{0x8C,0xD8, // mov eax, ds
0xC3}; // ret
unsigned char SetGS32[] =
{0x8E,0x6C,0x24,0x04, // mov gs, ss:[sp+4]
0xC3 }; // ret
unsigned char UseGS32[] =
{ 0x8B,0x44,0x24,0x04, // mov eax, ss:[sp+4]
0x65,0x8B,0x00, // mov eax, gs:[eax]
0xc3 }; // ret
unsigned char SetGS64[] =
{0x8E,0xe9, // mov gs, rcx
0xC3 }; // ret
unsigned char UseGS64[] =
{ 0x65,0x8B,0x01, // mov eax, gs:[rcx]
0xc3 };
typedef WORD(*fcnGetDS)(void);
typedef void(*fcnSetGS)(WORD);
typedef DWORD(*fcnUseGS)(LPVOID);
int (*NtSetLdtEntries)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD);
int main( void )
{
SYSTEM_INFO si;
GetSystemInfo(&si);
LPVOID p = VirtualAlloc(NULL, 1024, MEM_COMMIT|MEM_TOP_DOWN,PAGE_EXECUTE_READWRITE);
fcnGetDS GetDS = (fcnGetDS)((LPBYTE)p+16);
fcnUseGS UseGS = (fcnUseGS)((LPBYTE)p+32);
fcnSetGS SetGS = (fcnSetGS)((LPBYTE)p+48);
*(DWORD *)p = 0x12345678;
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
{
memcpy( GetDS, &GetDS32, sizeof(GetDS32));
memcpy( UseGS, &UseGS64, sizeof(UseGS64));
memcpy( SetGS, &SetGS64, sizeof(SetGS64));
}
else
{
memcpy( GetDS, &GetDS32, sizeof(GetDS32));
memcpy( UseGS, &UseGS32, sizeof(UseGS32));
memcpy( SetGS, &SetGS32, sizeof(SetGS32));
}
SetGS(GetDS());
if (UseGS(p) != 0x12345678) exit(-1);
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
{
// The gist of the question - What is the 64-bit equivalent of the following code
}
else
{
DWORD base = (DWORD)p;
LDT_ENTRY ll;
int ret;
*(FARPROC*)(&NtSetLdtEntries) = GetProcAddress(LoadLibrary("ntdll.dll"), "NtSetLdtEntries");
ll.BaseLow = base & 0xFFFF;
ll.HighWord.Bytes.BaseMid = base >> 16;
ll.HighWord.Bytes.BaseHi = base >> 24;
ll.LimitLow = 400;
ll.HighWord.Bits.LimitHi = 0;
ll.HighWord.Bits.Granularity = 0;
ll.HighWord.Bits.Default_Big = 1;
ll.HighWord.Bits.Reserved_0 = 0;
ll.HighWord.Bits.Sys = 0;
ll.HighWord.Bits.Pres = 1;
ll.HighWord.Bits.Dpl = 3;
ll.HighWord.Bits.Type = 0x13;
ret = NtSetLdtEntries(0x80, *(DWORD*)&ll, *((DWORD*)(&ll)+1),0,0,0);
if (ret < 0) { exit(-1);}
SetGS(0x84);
}
if (UseGS(0) != 0x12345678) exit(-1);
printf("PASS\n");
}