Wie kann ein User-Space-Programm "GS:" unter 64-Bit-Windows (derzeit XP-64) konfigurieren?
(Bei der Konfiguration ist GS:0 auf eine beliebige lineare 64-Bit-Adresse zu setzen).
Ich versuche, eine "JIT"-Umgebung auf X86-64 zu portieren, die ursprünglich für Win32 entwickelt wurde.
Ein unglücklicher Design-Aspekt ist, dass identischer Code in mehreren User-Space-Threads (z.B. "Fasern") laufen muss. Die Win32-Version des Codes verwendet hierfür den GS-Selektor und generiert das richtige Präfix für den Zugriff auf die lokalen Daten - "mov eax,GS:[offset]" zeigt auf die richtigen Daten für die aktuelle Aufgabe. Der Code der Win32-Version würde einen Wert in GS laden, wenn er nur einen Wert hätte, der funktionieren würde.
Bisher konnte ich feststellen, dass 64-Bit-Windows die LDT nicht unterstützt, so dass die unter Win32 verwendete Methode nicht funktioniert. Der X86-64-Befehlssatz enthält jedoch "SWAPGS" sowie eine Methode zum Laden von GS ohne Verwendung der Legacy-Segmentierung - dies funktioniert jedoch nur im Kernelbereich.
Laut den X64-Handbüchern gibt es, selbst wenn Win64 den Zugriff auf Deskriptoren erlauben würde - was nicht der Fall ist - keine Möglichkeit, die hohen 32-Bits der Segmentbasis zu setzen. Die einzige Möglichkeit, diese zu setzen, ist über GS_BASE_MSR (und die entsprechende FS_BASE_MSR - die anderen Segmentbasen werden im 64-Bit-Modus ignoriert). Der WRMSR-Befehl ist Ring0, so dass ich ihn nicht direkt verwenden kann.
Ich hoffe auf eine Zw*-Funktion, mit der ich "GS:" im Userspace ändern kann, oder auf eine andere dunkle Ecke der Windows-API. Ich glaube, Windows verwendet immer noch FS: für sein eigenes TLS, also muss ein Mechanismus vorhanden sein?
Dieser Beispielcode veranschaulicht das Problem. Ich entschuldige mich im Voraus für die Verwendung von Byte-Code - VS wird nicht Inline-Assembler für die 64-Bit-Kompilierung zu tun, und ich war versucht, dies als eine Datei für illustrative Zwecke zu halten.
Das Programm zeigt auf XP-32 "PASS" an, auf XP-x64 jedoch nicht.
#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");
}